/* Autor: Tadeusz Suchecki Wzorzec: Singleton Zastosowanie: Singleton to wzorzec konstrukcyjny przeznaczony do ograniczania mozliwosci tworzenia obiektow danej klasy do pojedynczej instancji. Ponadto musi zapewniac globalny dostep do istniejacej instancji obiektu. Czesto w aplikacji istnieje potrzeba stworzenia klasy, ktora posiadalaby wylacznie jedna instancje. Zwykle zwiazane to jest z zapewnieniem wiekszej wydajnosci aplikacji, np. przy dostepie do bazy danych, gdzie kazde laczenie sie z baza jest dla aplikacji kosztowne, bo wymaga czasochlonnego uwierzytelnienia i autoryzacji. W tym przypadku sensowniej jest stworzyc jeden obiekt przechowujacy sesje polaczenia i wykorzystac go do przeslania wielu zapytan. Rowniez w mniej krytycznych przypadkach korzystne jest stosowanie wzorca singletonu. Wskazowka do jego uzycia jest sytuacja, gdy potrzebna jest wylacznie jedna instancja obiektu, ktora wywoluje wiele roznych czesci aplikacji. W takich przypadkach tworzenie obiektu, a nastepnie niszczenie go jest marnotrawstwem zasobow pamieci. Opis: Wzorzec singletonu implementuje sie przez stworzenie klasy, ktora posiada statyczna metode, ktora najpierw sprawdza, czy istnieje juz instancja tej klasy - jesli nie istnieje to tworzy ja - i nastepnie zwraca ja przez referencje. Instancja klasy jest przechowywana w prywatnym lub chronionym, statycznym polu, do ktorego dostep ma tylko opisana wyzej metoda. Owa metoda dostepowa jest jedyna droga pozyskania instancji obiektu singletonu - aby uniemozliwic tworzenie dodatkowych instancji w zwykly sposob, czyli przez wywolanie konstruktora, deklaruje sie go jako prywatny lub chroniony. Dodatkowa korzyscia z zastosowania takiego rozwiazania jest to, ze caly proces jest niewidoczny dla uzytkownika i nie musi on wiedziec, czy instancja istnieje czy tez nie. */ /* Dobrym przykladem na zastosowanie singletonu jest polaczenie do bazy danych. W przykladzie nie bede zaglebial sie w samo polaczenie i ogranicze do wypisania informacji o id klienta i numerze wywolania. Informacje te powinny pozwolic zauwazyc sposob dzialania obiektu. */ #include <pthread.h> #include <memory> #include <iostream> #include <string> #include <sstream> /* Mutex potrzebny do synchronizacji wielu watkow */ class Mutex { public: Mutex() { pthread_mutex_init(&m, 0); } void lock() { pthread_mutex_lock(&m); } void unlock() { pthread_mutex_unlock(&m); } private: pthread_mutex_t m; }; /* Pomocniczy locker dla wygody */ class MutexLocker { public: MutexLocker(Mutex& pm): m(pm) { m.lock(); } ~MutexLocker() { m.unlock(); } private: Mutex& m; }; /* Singleton - polaczenie do bazy danych ktore w tym programie moze byc tylko jedno. Gdy jest potrzebne a nie istnieje jest tworzone gdy istnieje zwracana jest referencja na istniejace. Po wywolaniu GetData(id) zwraca informacje o numerze wywolania i id klienta wywolujacego. */ class DB_Connection { public: static DB_Connection& Instance(); int example_data; ~DB_Connection() { } std::string GetData(int id); protected: DB_Connection(): example_data(1) {} /* chroniony aby nie dalo sie stworzyc instancji inaczje niz przez Instance(); */ private: static std::auto_ptr<DB_Connection> theSingleInstance; static Mutex m; }; /* Chyba nie wymaga komentarza */ std::string DB_Connection::GetData(int id) { std::ostringstream os; os<<"odwolanie nr: "<<example_data++<<" poczynione przez urzytkownika o id "<<id; return os.str(); } /* Zapewnienie istnienia jednej instancji. Jesli instancja nie istnieje jest tworzona. Zwracana jest referencja na instancje. */ DB_Connection& DB_Connection::Instance() { if (theSingleInstance.get() == 0) { MutexLocker obtain_lock(m); if (theSingleInstance.get() == 0) { theSingleInstance.reset(new DB_Connection); } } return *theSingleInstance; } std::auto_ptr<DB_Connection> DB_Connection::theSingleInstance; Mutex DB_Connection::m; /* Procedura testowa: kolejno klienci 1,2,3 wywoluja baze */ int main() { std::cout << DB_Connection::Instance().GetData(1) << std::endl; std::cout << DB_Connection::Instance().GetData(2) << std::endl; std::cout << DB_Connection::Instance().GetData(3) << std::endl; std::cout << DB_Connection::Instance().GetData(1) << std::endl; std::cout << DB_Connection::Instance().GetData(2) << std::endl; std::cout << DB_Connection::Instance().GetData(3) << std::endl; std::cout << DB_Connection::Instance().GetData(1) << std::endl; std::cout << DB_Connection::Instance().GetData(2) << std::endl; std::cout << DB_Connection::Instance().GetData(3) << std::endl; return 0; }