Kiedy nie należy stosować wzoru Singletona? (Poza oczywistym)

Wiem dobrze, że chcesz użyć Singleton, aby zapewnić globalny punkt dostępu do jakiegoś stanu lub usługi. Korzyści wynikające ze wzoru Singletona nie muszą być wymienione w tym pytaniu.

Interesują mnie sytuacje, kiedy Singleton może wydawać się dobrym wyborem na początku, ale może wrócić, by cię ugryźć. Raz po raz, widziałem autorów w książkach i plakatach na tak powiedzieć, że wzór Singleton jest często bardzo zły pomysł.

Gang czterech stanów że będziesz chciał użyć Singletona, gdy:

  • musi istnieć dokładnie jedna instancja klasy I musi być dostępna dla klientów ze znanego punktu dostępu.
  • kiedy jedyna instancja powinna być rozszerzalna przez podklasowanie, a klienci powinni być w stanie używać rozszerzonej instancji bez modyfikowania ich kodu.

Te punkty, choć z pewnością godne uwagi, nie są tymi praktycznymi, których szukam.

Czy ktoś ma zestaw zasad lub zastrzeżeń, których używasz do oceń, czy naprawdę, naprawdę chcesz użyć Singletona?

Author: Andrew Barber, 2010-11-02

8 answers

Wersja Skrócona:

Wiesz, jak często używasz globali? Ok, teraz używaj singletonów jeszcze mniej. A tym bardziej w rzeczywistości. Prawie nigdy. Podzielają wszystkie problemy, jakie globalni mają z ukrytym sprzężeniem (bezpośrednio wpływającym na testowalność i konserwowalność), a często ograniczenie "tylko jeden może istnieć" jest w rzeczywistości błędnym założeniem.

Szczegółowa Odpowiedź:

Najważniejszą rzeczą do uświadomienia sobie o singletonie jest to, że jest to stan globalny. Jest to wzór do ujawnienia pojedynczej instancji globalnie nieograniczonego dostępu . Ma to wszystkie problemy w programowaniu, które mają globalni, ale także przyjmuje kilka interesujących nowych szczegółów implementacji i poza tym bardzo małą rzeczywistą wartość (lub, w rzeczywistości, może to przynieść niepotrzebne dodatkowe koszty z aspektem pojedynczej instancji). Implementacja jest na tyle Inna, że ludzie często mylą ją z obiektową metodą enkapsulacji, gdy tak naprawdę jest to tylko fantazyjna pojedyncza instancja global.

Jedyną sytuacją, w której powinieneś rozważyć singleton, jest sytuacja, w której posiadanie więcej niż jednej instancji już globalnych danych byłoby w rzeczywistości logicznym lub sprzętowym błędem dostępu. Nawet wtedy zazwyczaj nie powinieneś zajmować się singletonem bezpośrednio, ale zamiast tego dostarczaj interfejs wrappera, który jest dozwolony do tworzenia instancji tyle razy, ile potrzebujesz, ale tylko uzyskuje dostęp do stanu globalnego. W ten sposób można nadal używać zależność wstrzyknięcie a jeśli kiedykolwiek możesz usunąć stan Globalny z zachowania klasy, nie jest to wielka zmiana w całym systemie.

Istnieją subtelne problemy z tym, jednak, gdy wydaje się, że nie polegasz na danych globalnych, ale jesteś. Tak, że (używając dependency injection interfejsu, który zawija singleton) jest tylko sugestią, a nie regułą. Ogólnie jest jeszcze lepiej, bo przynajmniej widać, że Klasa opiera się na singleton natomiast samo użycie funkcji:: instance () wewnątrz brzucha funkcji członka klasy ukrywa tę zależność. Pozwala również na wyodrębnianie klas opartych na stanie globalnym i ulepszanie testy jednostkowe dla nich, i można przekazać w mock do-nothing obiektów, gdzie jeśli piec poleganie na singleton bezpośrednio do klasy jest to o wiele trudniejsze.

Podczas pieczenia singleton :: instancja wywołanie, które również tworzy instancję do klasy you uniemożliwiadziedziczenie . Work-arounds zazwyczaj łamie "pojedynczą instancję" część singletonu. Rozważ sytuację, w której masz wiele projektów opartych na współdzielonym kodzie w klasie NetworkManager. Nawet jeśli chcesz, aby ten NetworkManager był stanem globalnym i pojedynczą instancją, powinieneś być Bardzo sceptycznie nastawiony do przekształcenia go w singleton. Tworząc prosty singleton, który tworzy instancje, w zasadzie uniemożliwiasz uzyskanie jakiegokolwiek innego projektu z tej klasy.

Wielu uważa, że ServiceLocator jest anty-wzorem, jednak uważam, że jest o pół kroku lepszy niż Singleton i skutecznie przyćmiewa cel wzoru Go4. Istnieje wiele sposobów wdrożenia lokalizatora usług, ale podstawowa koncepcja polega na rozbiciu konstrukcji obiektu i dostępu do obiektu na dwa etapy. W ten sposób podczas wykonywania można podłączyć odpowiednią usługę pochodną, a następnie uzyskać do niej dostęp z jednego globalny punkt kontaktowy. Ma to zaletę wyraźnego zlecenia budowy obiektów, a także pozwala na wyprowadzenie z obiektu podstawowego. To jest nadal źle z większości podanych powodów, ale jest Mniej źle niż Singleton i jest zamiennikiem drop-in.

Jeden konkretny przykład akceptowalnego Singletona (Czytaj: servicelocator) może być w zawijaniu interfejsu w stylu c, takiego jak SDL_mixer. Jednym z przykładów singletonu często naiwnie realizowanego, gdzie prawdopodobnie nie powinno być w klasie logowania (co się dzieje, gdy chcesz się zalogować na konsolę i na dysk? Lub jeśli chcesz logować podsystemy oddzielnie.)

Najważniejsze problemy polegania na państwie globalnym, jednak prawie zawsze pojawiają się, gdy próbujesz wdrożyć właściwe testy jednostkowe (i powinieneś próbować to zrobić). O wiele trudniej jest poradzić sobie z aplikacją, gdy wnętrzności klas, do których tak naprawdę nie masz dostępu, próbują aby wykonać nieograniczony zapis i odczyt dysku, połącz się z serwerami na żywo i wyślij prawdziwe dane lub wydmuchaj dźwięk z głośników willy nilly. O wiele, wiele lepiej jest użyć dependency injection, aby móc stworzyć klasę do-nothing (i zobaczyć, że musisz to zrobić w konstruktorze klasy) w przypadku planu testowego i wskazać na to bez konieczności opisywania całego globalnego stanu, od którego zależy Twoja klasa.

Powiązane Linki:

Użycie wzorca a pojawienie się

Wzory są przydatne jako pomysły i terminy, ale niestety ludzie wydają się odczuwać potrzebę" używania " wzorca, gdy naprawdę wzorce są implementowane zgodnie z potrzebami. Często singleton specjalnie jest shoehorned w po prostu dlatego, że jest to powszechnie omawiany wzór. Zaprojektuj Swój system z świadomość wzorców, ale nie projektuj swojego systemu specjalnie po to, aby się do nich ugiąć tylko dlatego, że istnieją. Są użytecznymi narzędziami koncepcyjnymi, ale tak jak nie używasz każdego narzędzia w zestawie narzędzi tylko dlatego, że możesz, nie powinieneś robić tego samego z wzorami. Używaj ich w razie potrzeby i nie mniej lub bardziej.

Przykładowy Lokalizator Usług Dla Jednej Instancji

#include <iostream>
#include <assert.h>

class Service {
public:
    static Service* Instance(){
        return _instance;
    }
    static Service* Connect(){
        assert(_instance == nullptr);
        _instance = new Service();
    }
    virtual ~Service(){}

    int GetData() const{
        return i;
    }
protected:
    Service(){}
    static Service* _instance;
    int i = 0;
};

class ServiceDerived : public Service {
public:
    static ServiceDerived* Instance(){
        return dynamic_cast<ServiceDerived*>(_instance);
    }
    static ServiceDerived* Connect(){
        assert(_instance == nullptr);
        _instance = new ServiceDerived();
    }
protected:
    ServiceDerived(){i = 10;}
};

Service* Service::_instance = nullptr;

int main() {
    //Swap which is Connected to test it out.
    Service::Connect();
    //ServiceDerived::Connect();
    std::cout << Service::Instance()->GetData() << "\n" << ((ServiceDerived::Instance())? ServiceDerived::Instance()->GetData() :-1);
    return 0;
}
 66
Author: M2tM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-06 23:44:42

Jedno słowo: testowanie

Jedną z cech testowalności jest luźne łączenie klas, pozwalające wyizolować pojedynczą klasę i przetestować ją całkowicie. Kiedy jedna klasa używa Singletona (i mówię o klasycznym singletonie, który wymusza jej własną osobliwość poprzez statyczną metodę getInstance ()), użytkownik Singletona i singleton stają się nierozerwalnie ze sobą sprzężone. Nie jest już możliwe przetestowanie użytkownika Bez również testowania singleton.

Singletony są katastrofą do przetestowania. Ponieważ są statyczne, nie możesz ich wstawić do podklasy. Ponieważ są globalne, nie możesz łatwo zmienić odniesienia, do którego wskazują, bez przekompilowania lub ciężkiego podnoszenia. Wszystko, co używa Singletona magicznie dostaje globalne odniesienie do czegoś, co jest trudne do kontrolowania. Utrudnia to ograniczenie zakresu badania.

 12
Author: Paul Rubel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-02 01:02:22

Największe błędy z Singletonem, jakie widziałem, to projektowanie systemu dla jednego użytkownika (powiedzmy program komputerowy) i używanie Singletona do wielu rzeczy (np. ustawień), a następnie chcesz stać się multi-użytkownikiem, jak strona internetowa lub usługa.

Jest to podobne do tego, co stało się z funkcjami C z wewnętrznymi buforami statycznymi, gdy zostały użyte w programach wielowątkowych.

 6
Author: Lou Franco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-02 00:33:01

Powiedziałbym, że unikaj singletonów za wszelką cenę. Ogranicza skalowanie aplikacji. Naprawdę przeanalizuj problem, z którym masz do czynienia, pomyśl o skalowalności i podejmuj decyzje w oparciu o skalowalność aplikacji.

Pod koniec dnia singleton działa jako wąskie gardło zasobów, jeśli został zaprojektowany nieprawidłowo.

Czasami wprowadzasz to wąskie gardło bez pełnego zrozumienia, jakie konsekwencje tego będą miały dla Twojej aplikacji.

I napotkałem problemy podczas radzenia sobie z aplikacjami wielowątkowymi, które próbują uzyskać dostęp do zasobu singleton, ale wpadają w impasy. Dlatego staram się unikać Singletona tak bardzo, jak to możliwe.

Jeśli wprowadzasz singletony do swojego projektu, upewnij się, że rozumiesz implikacje związane z uruchomieniem, wykonaj kilka diagramów i dowiedz się, gdzie może to spowodować problem.

 5
Author: WeNeedAnswers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-18 09:59:33

Singleton jest często używany jako chwytak do rzeczy, których ludzie nie mogą sobie zadawać trudu, aby właściwie zamknąć w miejscu, w którym jest to rzeczywiście potrzebne, z odpowiednimi akcesoriami.

Wynikiem końcowym jest tarball, który ostatecznie zbiera wszystkie statics w całym systemie. Ilu ludzi tutaj nigdy nie widziało klasy o nazwie Globals w jakimś kodzie ood, z którym musieli pracować? Ugh.

 3
Author: Steve Townsend,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-02 00:36:07

Nie unikałbym tego całkowicie w żadnym projekcie. Należy jednak uważać na jego użycie. Może stać się celem Boga w wielu scenariuszach, a tym samym pokonać cel.

Należy pamiętać, że ten wzorzec projektowy jest rozwiązaniem do rozwiązania niektórych problemów, ale nie wszystkich problemów. W rzeczywistości jest to takie samo dla wszystkich wzorców projektowych.

 0
Author: zerodin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-02 00:58:54

Nie uważam się za doświadczonego programistę, ale moja obecna opinia jest taka, że w rzeczywistości nie potrzebujesz Singletona ... tak, z początku wydaje się łatwiej pracować (podobnie jak z globalami), ale potem przychodzi moment "o mój", kiedy potrzeba innej instancji.

Zawsze możesz przekazać lub wstrzyknąć instancję, nie widzę sytuacji, w której użycie Singletona byłoby znacznie łatwiejsze lub konieczne

Nawet jeśli odrzucimy wszystko, wciąż pozostaje kwestia testowalność kodu

 0
Author: NoxArt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-12 21:53:47

Zobacz pierwszą odpowiedź w dyskusji" What ' s Alternative to Singleton".

 -1
Author: Yevgeniy Brikman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:22