Edycja: Z innego pytania podałem odpowiedź, która zawiera linki do wielu pytań / odpowiedzi na temat singletonów: Więcej informacji na temat singletonów tutaj:
Przeczytałem więc temat Singletons: dobry projekt czy kula?
A kłótnia wciąż trwa.
Widzę Singletony jako wzorzec projektowy (dobry i zły).
Problemem w Singleton nie jest wzorzec, ale użytkownicy (przepraszam wszystkich). Wszyscy i ich ojciec myślą, że mogą poprawnie wdrożyć jedną (a z wielu wywiadów, które przeprowadziłem, większość ludzi nie może). Ponieważ wszyscy myślą, że mogą wdrożyć prawidłowy Singleton, nadużywają Wzorca i używają go w sytuacjach, które nie są odpowiednie (zastępując zmienne globalne Singletonami!).
Głównymi pytaniami, na które należy odpowiedzieć, są:
- Kiedy należy stosować Singleton
- Jak poprawnie wdrożyć Singleton?
Mam nadzieję, że ten artykuł pozwoli nam zebrać razem w jednym miejscu (zamiast wyszukiwać w Google i przeszukiwać wiele witryn) wiarygodne źródło informacji o tym, kiedy (a potem jak) poprawnie używać Singletona. Odpowiednia byłaby również lista Anti-Usages i typowych złych implementacji wyjaśniająca, dlaczego nie działają i dla dobrych implementacji ich słabości.
Więc
ruszaj piłką: podniosę rękę i powiem, że tego właśnie używam, ale prawdopodobnie mam problemy.
Podoba mi się „Scott Myers” zajmujący się tym tematem w swoich książkach „Effective C ++”
Dobre sytuacje do korzystania z singletonów (nie wiele):
- Ramy rejestrowania
- Pula recyklingu nici
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
OK. Pozyskajmy krytykę i inne implementacje razem.
:-)
źródło
Odpowiedzi:
Wszyscy się mylicie. Przeczytaj pytanie Odpowiedź:
Użyj singletonu, jeśli:
Nie używaj Singletona, jeśli:
Jak stworzyć najlepszy singleton:
źródło
Singletony dają możliwość połączenia dwóch złych cech w jednej klasie. To źle pod każdym względem.
Singleton zapewnia:
Numer jeden jest prosty. Globale są ogólnie złe. Nigdy nie powinniśmy udostępniać obiektów globalnie, chyba że my naprawdę tego potrzebujemy.
Numer dwa może wydawać się sensowny, ale zastanówmy się nad tym. Kiedy ostatni raz ** przypadkowo * stworzyłeś nowy obiekt zamiast odwoływania się do istniejącego? Ponieważ jest to oznaczone jako C ++, skorzystajmy z przykładu z tego języka. Czy często przypadkowo piszesz?
Kiedy zamierzałeś pisać
Oczywiście nie. Nie potrzebujemy ochrony przed tym błędem, ponieważ tego rodzaju błąd po prostu się nie zdarza. Jeśli tak, prawidłową odpowiedzią jest pójść do domu i spać przez 12-20 godzin, mając nadzieję, że poczujesz się lepiej.
Jeśli potrzebny jest tylko jeden obiekt, po prostu utwórz jedną instancję. Jeśli jeden obiekt powinien być globalnie dostępny, uczyń go globalnym. Ale to nie znaczy, że tworzenie innych instancji powinno być niemożliwe.
Ograniczenie „możliwa jest tylko jedna instancja” tak naprawdę nie chroni nas przed prawdopodobnymi błędami. Ale to ma uczynić nasz kod bardzo ciężko byłaby i utrzymaniu. Ponieważ dość często dowiadujemy się później , że potrzebowaliśmy więcej niż jednego wystąpienia. Możemy zrobić więcej niż jedną bazę danych, możemy nie mieć więcej niż jeden obiekt konfiguracji, chcemy kilku rejestratorów. Nasze testy jednostkowe mogą chcieć być w stanie tworzyć i odtwarzać te obiekty w każdym teście, biorąc przykład.
Więc pojedyncza powinny być stosowane tylko wtedy, gdy potrzebujemy zarówno cech, co oferuje: Jeśli trzeba globalny dostęp (co jest rzadkością, ponieważ globalne są na ogół zniechęca) , a my potrzebujemy aby nikt nigdy tworzenia więcej niż jednej instancji klasa (co wydaje mi się problemem projektowym). Jedyny powód, dla którego to widzę, to to, że utworzenie dwóch instancji spowodowałoby uszkodzenie naszego stanu aplikacji - prawdopodobnie dlatego, że klasa zawiera pewną liczbę elementów statycznych lub podobny głupotę. W takim przypadku oczywistą odpowiedzią jest naprawienie tej klasy. Nie powinno to zależeć od bycia jedyną instancją.
Jeśli potrzebujesz globalnego dostępu do obiektu, zmień go na globalny
std::cout
. Ale nie ograniczaj liczby instancji, które można utworzyć.Jeśli absolutnie, pozytywnie potrzebujesz ograniczyć liczbę instancji klasy do jednej, i nie ma możliwości, aby utworzenie drugiej instancji kiedykolwiek mogło być obsługiwane bezpiecznie, to wymusz to. Ale nie udostępniaj go również globalnie.
Jeśli potrzebujesz obu cech, to 1) uczyń z niego singletona, a 2) daj mi znać, do czego go potrzebujesz, ponieważ trudno mi sobie wyobrazić taki przypadek.
źródło
Problemem singletonów nie jest ich implementacja. Chodzi o to, że łączą dwa różne pojęcia, z których żadne nie jest oczywiście pożądane.
1) Singletony zapewniają globalny mechanizm dostępu do obiektu. Chociaż mogą być nieznacznie bardziej wątkowo bezpieczne lub marginalnie bardziej niezawodne w językach bez dobrze zdefiniowanej kolejności inicjalizacji, to użycie jest nadal moralnym odpowiednikiem zmiennej globalnej. Jest to zmienna globalna ubrana w niezręczną składnię (powiedzmy foo :: get_instance () zamiast g_foo), ale służy dokładnie temu samemu celowi (pojedynczy obiekt dostępny w całym programie) i ma te same wady.
2) Singletony zapobiegają wielu wystąpieniom klasy. IME rzadko zdarza się, że tego rodzaju funkcje należy upiec w klasie. Zwykle jest to coś bardziej kontekstowego; wiele rzeczy, które są uważane za jeden i tylko jeden, tak naprawdę jest tak naprawdę tylko jednym. IMO bardziej odpowiednim rozwiązaniem jest utworzenie tylko jednej instancji - dopóki nie uświadomisz sobie, że potrzebujesz więcej niż jednej instancji.
źródło
Jedna rzecz ze wzorami: nie generalizuj . Mają wszystkie przypadki, gdy są przydatne i kiedy zawodzą.
Singleton może być nieprzyjemny, gdy trzeba przetestować kod. Na ogół utkniesz w jednej instancji klasy i możesz wybierać między otwarciem drzwi w konstruktorze lub inną metodą resetowania stanu i tak dalej.
Innym problemem jest to, że Singleton w rzeczywistości jest jedynie ukrytą zmienną globalną . Kiedy masz za dużo globalnego wspólnego stanu w swoim programie, wszystko ma tendencję do cofania się, wszyscy o tym wiemy.
Może to utrudnić śledzenie zależności . Kiedy wszystko zależy od twojego Singletona, trudniej jest go zmienić, podzielić na dwa itd. Na ogół utkniesz z tym. Utrudnia to także elastyczność. Sprawdź niektóre ramy wstrzykiwania zależności , aby spróbować rozwiązać ten problem.
źródło
Singletony zasadniczo pozwalają na uzyskanie złożonego globalnego stanu w językach, które w innym przypadku utrudniają lub uniemożliwiają tworzenie złożonych zmiennych globalnych.
W szczególności Java używa singletonów jako zamiennika zmiennych globalnych, ponieważ wszystko musi być zawarte w klasie. Najbardziej zbliżone do zmiennych globalnych są publiczne zmienne statyczne, które można stosować tak, jakby były globalne
import static
C ++ ma zmienne globalne, ale kolejność wywoływania konstruktorów zmiennych klasy globalnej jest nieokreślona. Jako taki singleton pozwala odroczyć utworzenie zmiennej globalnej do momentu, gdy zmienna ta będzie potrzebna.
Języki takie jak Python i Ruby bardzo rzadko korzystają z singletonów, ponieważ zamiast tego można używać zmiennych globalnych w module.
Kiedy więc warto używać singletona? Prawie dokładnie wtedy, gdy dobrze / źle byłoby użyć zmiennej globalnej.
źródło
Nowoczesny projekt C ++ autorstwa Alexandrescu ma bezpieczny dla wątków, dziedziczny ogólny singleton.
Dla mojej wartości 2 pensów uważam, że ważne jest, aby zdefiniować okresy życia swoich singletonów (kiedy jest to absolutnie konieczne, aby z nich skorzystać). Zwykle nie pozwalam, aby
get()
funkcja statyczna tworzyła cokolwiek, a konfigurację i zniszczenie pozostawiam w dedykowanej sekcji głównej aplikacji. Pomaga to uwypuklić zależności między singletonami - ale, jak podkreślono powyżej, najlepiej jest ich unikać, jeśli to możliwe.źródło
Jest jeden problem, o którym nigdy nie wspominałem, coś, na co wpadłem podczas poprzedniej pracy. Mieliśmy singletony C ++, które były współdzielone między bibliotekami DLL, i zwykła mechanika zapewniania, że pojedyncze wystąpienie klasy po prostu nie działa. Problem polega na tym, że każda biblioteka DLL otrzymuje własny zestaw zmiennych statycznych wraz z plikiem EXE. Jeśli twoja funkcja get_instance jest wbudowana lub stanowi część biblioteki statycznej, każda biblioteka DLL skończy z własną kopią „singletonu”.
Rozwiązaniem jest upewnienie się, że kod singletonu jest zdefiniowany tylko w jednej bibliotece DLL lub EXE, lub utworzenie menedżera singletonów z tymi właściwościami w celu sparowania wystąpień.
źródło
Pierwszy przykład nie jest bezpieczny dla wątków - jeśli dwa wątki wywołują getInstance w tym samym czasie, to statyczny będzie PITA. Pomocna byłaby jakaś forma muteksu.
źródło
Jak zauważyli inni, głównymi wadami singletonów są niemożność ich rozszerzenia i utrata mocy tworzenia instancji więcej niż jednego wystąpienia, np. Do celów testowych.
Niektóre przydatne aspekty singletonów:
Jednak nie musisz używać singletonu, aby uzyskać te korzyści. Możesz napisać normalny obiekt, który wykonuje pracę, a następnie umożliwić ludziom dostęp do niego za pośrednictwem fabryki (osobny obiekt). Fabryka może martwić się o utworzenie jej i ponowne użycie itp., Jeśli zajdzie taka potrzeba. Ponadto, jeśli programujesz interfejs, a nie konkretną klasę, fabryka może stosować strategie, tzn. Możesz włączać i wyłączać różne implementacje interfejsu.
Wreszcie fabryka nadaje się do technologii wstrzykiwania zależności, takich jak Spring itp.
źródło
Singletony są przydatne, gdy wiele kodu jest uruchamianych podczas inicjowania i sprzeciwu. Na przykład, gdy używasz iBatis do konfigurowania obiektu trwałego, musi on przeczytać wszystkie konfiguracje, przeanalizować mapy, upewnić się, że wszystko jest poprawne itp. Przed przejściem do twojego kodu.
Jeśli zrobiłbyś to za każdym razem, wydajność byłaby znacznie niższa. Używając go w singletonie, bierzesz to trafienie raz, a potem wszystkie kolejne połączenia nie muszą tego robić.
źródło
Prawdziwym upadkiem Singletonów jest to, że niszczą dziedzictwo. Nie możesz uzyskać nowej klasy, która zapewni ci rozszerzoną funkcjonalność, chyba że masz dostęp do kodu, do którego odnosi się Singleton. Tak więc poza faktem, że Singleton sprawi, że kod będzie ciasno powiązany (naprawiony przez wzorzec strategii ... aka Dependency Injection), uniemożliwi również zamknięcie części kodu przed korektą (biblioteki współdzielone).
Dlatego nawet przykłady programów rejestrujących lub pul wątków są nieprawidłowe i powinny zostać zastąpione przez Strategie.
źródło
Większość ludzi korzysta z singletonów, kiedy chcą się dobrze czuć przy użyciu zmiennej globalnej. Istnieją uzasadnione zastosowania, ale w większości przypadków, gdy ludzie z nich korzystają, fakt, że może istnieć tylko jedna instancja, jest tylko trywialnym faktem w porównaniu z faktem, że jest ona dostępna na całym świecie.
źródło
Ponieważ singleton umożliwia utworzenie tylko jednej instancji, skutecznie kontroluje replikację instancji. na przykład nie potrzebujesz wielu instancji wyszukiwania - na przykład mapa odnośników Morse'a, więc pakowanie jej w klasę singleton jest trafne. I tylko dlatego, że masz jedną instancję klasy, nie oznacza to, że masz również ograniczoną liczbę odwołań do tej instancji. Możesz kolejkować połączenia (aby uniknąć problemów z wątkami) do instancji i konieczne zmiany efektów. Tak, ogólna forma singletonu jest ogólnoświatowa, z pewnością możesz zmodyfikować projekt, aby stworzyć singleton o bardziej ograniczonym dostępie. Nie zmęczyłem się tym wcześniej, ale na pewno wiem, że to możliwe. I wszystkim, którzy skomentowali stwierdzenie, że wzór singletonu jest całkowicie zły, powinniście wiedzieć:
źródło
Ale kiedy potrzebuję czegoś takiego jak Singleton, często używam licznika Schwarz, aby to zrobić.
źródło
Poniżej znajduje się lepsze podejście do implementacji bezpiecznego dla wątku wzoru singletonu z dezalokacją pamięci w samym destruktorze. Ale myślę, że destruktor powinien być opcjonalny, ponieważ instancja singletonu zostanie automatycznie zniszczona po zakończeniu programu:
Jeśli chodzi o sytuacje, w których musimy użyć klas singletonowych, możemy: - Jeśli chcemy utrzymać stan instancji przez cały czas wykonywania programu Jeśli jesteśmy zaangażowani w zapisywanie w dzienniku wykonania aplikacji, w którym tylko jedna instancja pliku musi być używane .... i tak dalej. Będzie to zauważalne, jeśli ktoś może zasugerować optymalizację w moim powyższym kodzie.
źródło
Używam Singletonów jako testu wywiadu.
Kiedy proszę dewelopera o podanie kilku wzorców projektowych, jeśli jedyne, co potrafią wymienić, to Singleton, nie są zatrudniani.
źródło
Anti-Usage:
Jednym z głównych problemów związanych z nadmiernym użyciem singletonów jest to, że wzorzec uniemożliwia łatwe rozszerzanie i zamianę alternatywnych implementacji. Nazwa klasy jest zakodowana na stałe wszędzie tam, gdzie używany jest singleton.
źródło
Myślę, że jest to najbardziej niezawodna wersja dla C #:
Oto wersja zoptymalizowana dla platformy .NET :
Możesz znaleźć ten wzór na dotfactory.com .
źródło
Wzór singletonu Meyersa działa wystarczająco dobrze przez większość czasu, a czasami tak nie jest, aby szukać czegoś lepszego. Tak długo, jak konstruktor nigdy nie będzie rzucał i nie będzie zależności między singletonami.
Singleton jest implementacją globalnie dostępnego obiektu (GAO od teraz), chociaż nie wszystkie GAO są singletonami.
Rejestratory same w sobie nie powinny być singletonami, ale idealnie powinny być globalnie dostępne środki do rejestrowania, aby oddzielić miejsce, w którym komunikat dziennika jest generowany, skąd i jak się loguje.
Leniwe ładowanie / leniwa ewaluacja to inna koncepcja i singleton zwykle też to implementuje. Ma wiele własnych problemów, w szczególności bezpieczeństwo wątków i problemy, jeśli zawodzi z wyjątkami takimi, że to, co w tamtym czasie wydawało się dobrym pomysłem, okazuje się wcale nie takie świetne. (Trochę jak implementacja COW w ciągach).
Mając to na uwadze, GOA można zainicjować w następujący sposób:
Nie trzeba tego robić tak brutalnie, a wyraźnie w załadowanej bibliotece zawierającej obiekty prawdopodobnie potrzebujesz innego mechanizmu do zarządzania ich żywotnością. (Umieść je w obiekcie, który otrzymasz po załadowaniu biblioteki).
A kiedy używam singletonów? Użyłem ich do dwóch rzeczy - tabeli singletonów, która wskazuje, jakie biblioteki zostały załadowane dlopen - programu obsługi komunikatów, do którego logujący mogą się zapisać i do którego możesz wysyłać wiadomości. Wymagany specjalnie dla programów obsługi sygnałów.
źródło
Nadal nie rozumiem, dlaczego singleton musi być globalny.
Zamierzałem stworzyć singletona, w którym ukryłem bazę danych wewnątrz klasy jako prywatną stałą zmienną statyczną i stworzyłem funkcje klasy, które wykorzystują bazę danych bez ujawniania jej użytkownikowi.
Nie rozumiem, dlaczego ta funkcjonalność byłaby zła.
źródło
Uważam je za przydatne, gdy mam zajęcia, które zawierają wiele pamięci. Na przykład w ostatniej grze, nad którą pracowałem, mam klasę mapy wpływów, która zawiera kolekcję bardzo dużych tablic ciągłej pamięci. Chcę, aby wszystkie zostały przydzielone podczas uruchamiania, wszystkie uwolnione przy wyłączaniu i zdecydowanie chcę tylko jedną kopię. Muszę też uzyskać do niego dostęp z wielu miejsc. Uważam, że wzór singletonu jest bardzo przydatny w tym przypadku.
Jestem pewien, że istnieją inne rozwiązania, ale uważam to za bardzo przydatne i łatwe do wdrożenia.
źródło
Jeśli jesteś tym, który stworzył singleton i używa go, nie rób z niego singletonu (nie ma to sensu, ponieważ możesz kontrolować osobliwość obiektu bez uczynienia go singletonem), ale ma to sens, gdy jesteś deweloperem biblioteki i chcesz dostarczyć użytkownikom tylko jeden obiekt (w tym przypadku to Ty stworzyłeś singleton, ale nie jesteś użytkownikiem).
Singletony są obiektami, więc używaj ich jako obiektów, wiele osób uzyskuje dostęp do singletonów bezpośrednio przez wywołanie metody, która je zwraca, ale jest to szkodliwe, ponieważ sprawiasz, że twój kod wie, że obiekt jest singletonem, wolę używać singletonów jako obiektów, przekazuję je za pomocą konstruktora, a ja używam ich jako zwykłych obiektów, w ten sposób twój kod nie wie, czy te obiekty są singletonami, czy nie, a to czyni zależności bardziej przejrzystymi i pomaga trochę w refaktoryzacji ...
źródło
W aplikacjach komputerowych (wiem, że tylko my dinozaury już je piszą!) Są one niezbędne do uzyskania względnie niezmiennych globalnych ustawień aplikacji - języka użytkownika, ścieżki do plików pomocy, preferencji użytkownika itp., Które w przeciwnym razie musiałyby się propagować w każdej klasie i każdym oknie dialogowym .
Edytuj - oczywiście powinny być tylko do odczytu!
źródło
Kolejne wdrożenie
źródło
Instance()
powinieneś zwrócić wskaźnik, a nie referencję. Wewnątrz.cpp
pliku, zainicjować instancję na NULL:Singleton* Singleton::instance_ = nullptr;
. IInstance()
powinny być realizowane jako:if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.