Co jest tak złego w singletonach? [Zamknięte]

1982

Wzorzec Singleton jest w pełni opłacone członek z GOF „s desenie książki , ale ostatnio wydaje się raczej osierocony przez świat dewelopera. Nadal używam całkiem wielu singletonów, szczególnie na zajęciach fabrycznych , i chociaż musisz być ostrożny w kwestii wielowątkowości (tak jak w przypadku każdej klasy), nie rozumiem, dlaczego są tak okropne.

Przepełnienie stosu wydaje się zakładać, że wszyscy zgadzają się, że Singletony są złe. Dlaczego?

Poprzyj swoje odpowiedzi „ faktami, referencjami lub specjalistyczną wiedzą

m87
źródło
6
Muszę powiedzieć, że używanie projektu singletonowego spaliło mnie ostatnio, gdy próbowałem dostosować kod. Gdy robię to w wolnym czasie, jestem prawie zbyt leniwy, aby to zmienić. Zła wiadomość dla wydajności.
Marcin,
71
W odpowiedziach jest wiele „wad”, ale chciałbym też zobaczyć kilka dobrych przykładów, kiedy wzór jest dobry, w przeciwieństwie do złych ...
DGM
50
Kilka miesięcy temu napisałem blog na ten temat: jalf.dk/blog/2010/03/… - i powiem to wprost. Nie mogę osobiście wymyślić jednej sytuacji, w której singleton jest właściwym rozwiązaniem. To nie znaczy, że taka sytuacja nie istnieje, ale ... nazywanie ich rzadkimi jest niedopowiedzeniem.
jalf
8
@AdamSmith nie oznacza to, że musisz , ale oznacza, że możesz uzyskać do niego dostęp w ten sposób. A jeśli nie zamierzasz uzyskać do niego dostępu w ten sposób, to nie ma powodu, aby uczynić go singletonem. Twój argument jest więc skuteczny: „nie ma nic złego w tworzeniu singletonu, jeśli nie traktujemy go jako singletona. Tak, świetnie. Mój samochód również nie zanieczyszcza się, jeśli go nie prowadzę. Ale wtedy łatwiej jest po prostu przede wszystkim nie
kupuję
35
Najgorsze w całym tym temacie jest to, że ludzie, którzy nienawidzą singletonów, rzadko podają konkretne sugestie, jak ich używać. Na przykład linki do artykułów w czasopismach i blogów wydawanych przez siebie w całym tym artykule SO mówią o tym, dlaczego nie używać singletonów (i wszystkie są doskonałymi powodami), ale są bardzo szczupłe w kwestii zamienników. Jednak dużo ręcznego falowania. Ci z nas, którzy próbują nauczyć nowych programistów, dlaczego nie powinniśmy używać singletonów, nie mają wielu dobrych kontrprzykładów innych firm, tylko wymyślone przykłady. To jest męczące.
Ti Strga

Odpowiedzi:

1293

Parafrazowane przez Briana Buttona:

  1. Są one na ogół używane jako instancja globalna, dlaczego to takie złe? Ponieważ ukrywasz zależności aplikacji w kodzie, zamiast ujawniać je za pośrednictwem interfejsów. Tworzenie czegoś globalnego, aby uniknąć przekazywania go, to zapach kodu .

  2. Naruszają zasadę pojedynczej odpowiedzialności , ponieważ kontrolują własne stworzenie i cykl życia.

  3. Z natury powodują ścisłe powiązanie kodu . To sprawia, że ​​udawanie ich w testach jest w wielu przypadkach raczej trudne.

  4. Niosą stan przez cały okres użytkowania aplikacji. Kolejne uderzenie w testowanie, ponieważ możesz skończyć z sytuacją, w której trzeba zamówić testy, co jest dużym „nie” dla testów jednostkowych. Dlaczego? Ponieważ każdy test jednostkowy powinien być niezależny od drugiego.

Jim Burger
źródło
324
Nie zgadzam sie z Toba. Ponieważ komentarze są dozwolone tylko dla 600 znaków, napisałem post na blogu, aby skomentować ten temat, zobacz poniższy link. jorudolph.wordpress.com/2009/11/22/singleton-considerations
Johannes Rudolph
61
W punktach 1 i 4 wydaje mi się, że singletony są przydatne i prawie idealne do buforowania danych (szczególnie z DB). Wzrost wydajności znacznie odbiega od zawiłości związanych z modelowaniem testów jednostkowych.
Dai Bok,
56
@Dai Bok: „buforowanie danych (szczególnie z bazy danych)” korzysta z wzorca proxy, aby to zrobić ...
paxos1977,
55
Wow, świetne odpowiedzi. Prawdopodobnie użyłem tutaj zbyt agresywnych sformułowań, więc proszę pamiętać, że odpowiadałem na pytanie negatywnie ukierunkowane. Moja odpowiedź miała być krótką listą „anty wzorów”, które powstają w wyniku złego użycia Singletona. Pełne ujawnienie informacji; Ja też od czasu do czasu używam Singletonów. Na SO jest więcej neutralnie postawionych pytań, które byłyby doskonałym forum, na którym Singletony można uznać za dobry pomysł. Na przykład stackoverflow.com/questions/228164/…
Jim Burger
21
Nie są tak świetne do buforowania w środowisku wielowątkowym. Możesz łatwo pokonać pamięć podręczną z wieloma wątkami walczącymi o ograniczony zasób. [utrzymywany przez singletona]
mnich
449

Singletony rozwiązują jeden (i tylko jeden) problem.

Spór o zasoby.

Jeśli masz jakieś zasoby, które

( 1 ) może mieć tylko jedną instancję i

( 2 ) musisz zarządzać tą pojedynczą instancją,

potrzebujesz singletona .

Nie ma wielu przykładów. Plik dziennika jest duży. Nie chcesz po prostu porzucić jednego pliku dziennika. Chcesz poprawnie przepłukać, zsynchronizować i zamknąć. To jest przykład pojedynczego zasobu udostępnionego, którym trzeba zarządzać.

Rzadko potrzebujesz singletona. Powodem, dla którego są źli, jest to, że czują się globalnie i są w pełni opłaconym członkiem książki GoF Design Patterns .

Kiedy myślisz, że potrzebujesz globalnej, prawdopodobnie popełnisz straszny błąd projektowy.

S.Lott
źródło
43
Sprzęt jest również przykładem, prawda? Systemy wbudowane mają wiele sprzętu, który może wykorzystywać singletony - a może jeden duży? <grin>
Jeff
170
Kompletnie się zgadzam. Krąży wiele zdeterminowanych rozmów „ta praktyka jest zła”, nie wiedząc, że praktyka może mieć swoje miejsce. Często praktyka jest „zła”, ponieważ często jest niewłaściwie stosowana. Nie ma nic z natury złego we wzorze Singleton, o ile jest on odpowiednio stosowany.
Damovisa,
53
Prawdziwe singletony z zasady są bardzo rzadkie (a kolejka drukarki na pewno nie jest jedna, podobnie jak plik dziennika - patrz log4j). Częściej niż nie sprzęt jest raczej przypadkiem niż zasadą. Cały sprzęt podłączony do komputera jest co najwyżej przypadkowym singletonem (pomyśl o wielu monitorach, myszach, drukarkach, kartach dźwiękowych). Nawet detektor cząstek o wartości 500 mln $ jest singletonem z powodu koincydencji i ograniczeń budżetowych - które nie mają zastosowania w oprogramowaniu, a zatem: brak singletonu. W telekomunikacji ani numer telefonu, ani telefon fizyczny nie są singletonami (pomyśl ISDN, call center).
digitalarbeiter
23
Sprzęt może mieć tylko jedną instancję różnych zasobów, ale sprzęt nie jest oprogramowaniem. Nie ma powodu, aby pojedynczy port szeregowy na płycie programistycznej był modelowany jako Singleton. Rzeczywiście, modelowanie go tak utrudni tylko portowanie na nową płytę, która ma dwa porty!
dash-tom-bang
19
Więc napisałbyś dwie identyczne klasy, powielając dużo kodu, żebyś mógł mieć dwa singletony reprezentujące dwa porty? Co powiesz na użycie dwóch globalnych obiektów SerialPort? A może w ogóle nie modelować sprzętu jako klas? A dlaczego miałbyś korzystać z singletonów, które również oznaczają globalny dostęp? Czy chcesz, aby każda funkcja w kodzie miała dostęp do portu szeregowego?
jalf
352

Niektóre kodujące snoby patrzą na nich z góry jak na gloryfikowany glob. W ten sam sposób, w jaki wiele osób nienawidzi oświadczenia goto , istnieją inne, które nienawidzą pomysłu korzystania z globalnego . Widziałem kilku programistów, którzy starali się uniknąć globalnego, ponieważ rozważali użycie jednego jako przyznania się do porażki. Dziwne ale prawdziwe.

W praktyce wzorzec Singleton jest tylko techniką programowania, która jest użyteczną częścią zestawu narzędzi pojęć. Od czasu do czasu może się okazać, że jest to idealne rozwiązanie, więc skorzystaj z niego. Ale używanie go tylko po to, aby pochwalić się użyciem wzorca projektowego, jest tak samo głupie, jak odmawianie jego użycia, ponieważ jest to po prostu globalny .

Phil Wright
źródło
17
Niepowodzenie, które widzę w Singleton, polega na tym, że ludzie używają ich zamiast globali, ponieważ są w jakiś sposób „lepsi”. Problemem jest (jak widzę), że rzeczy, które Singleton przynosi w tych przypadkach do stołu, są nieistotne. (Construct-on-first-use jest trywialny do wdrożenia w trybie innym niż singleton, nawet jeśli tak naprawdę nie używa konstruktora do tego.)
dash-tom-bang
21
Powodem, dla którego „patrzymy na nie z góry” jest to, że „bardzo często” widzimy, że są niewłaściwie używane i zbyt często. „My” wiemy, że mają swoje miejsce przyczyny.
Bjarke Freund-Hansen
5
@Phil, powiedziałeś „od czasu do czasu może się okazać, że jest to idealne rozwiązanie, więc skorzystaj z niego”. Ok, więc dokładnie w którym przypadku uznalibyśmy singleton za użyteczny?
Pacerier
22
@Pacerier, ilekroć spełnione są wszystkie poniższe warunki: (1) potrzebujesz tylko jednego z czegoś, (2) musisz przekazać to jedno jako argument w dużej liczbie wywołań metod, (3) jesteś skłonny zaakceptować szansa na konieczność pewnego refaktoryzacji w zamian za natychmiastowe zmniejszenie rozmiaru i złożoności kodu, ponieważ nie wszędzie będzie to przekleństwo.
antinome
18
Nie wydaje mi się, żeby to cokolwiek odpowiadało, po prostu mówi, że „czasami może pasować, innym razem może nie”. OK, ale dlaczego i kiedy? Co sprawia, że ​​ta odpowiedź jest czymś więcej niż argumentem do umiaru ?
Guildenstern
219

Misko Hevery z Google ma kilka interesujących artykułów na ten temat ...

Singletony są patologicznymi kłamcami ma przykładowy test jednostkowy, który ilustruje, w jaki sposób singletony mogą utrudnić określenie łańcuchów zależności oraz uruchomienie lub przetestowanie aplikacji. Jest to dość ekstremalny przykład znęcania się, ale jego uwaga jest nadal aktualna:

Singletony są niczym więcej jak państwem globalnym. Stan globalny sprawia, że ​​twoje obiekty mogą potajemnie zdobyć rzeczy, które nie są zadeklarowane w ich interfejsach API, w wyniku czego Singletony przekształcają twoje interfejsy API w patologicznych kłamców.

Tam, gdzie zniknęły wszystkie Singletony, chodzi o to, że zastrzyk zależności ułatwił uzyskanie wystąpień do konstruktorów, które ich wymagają, co zmniejsza podstawową potrzebę złych, globalnych Singletonów, które potępiły się w pierwszym artykule.

Jason
źródło
8
Artykuły Misko na ten temat są obecnie najlepszą rzeczą na ten temat.
Chris Mayer,
22
Pierwszy link w rzeczywistości nie rozwiązuje problemu z singletonami, ale raczej zakłada statyczną zależność wewnątrz klasy. Podany przykład można naprawić, przekazując parametry, ale nadal używać singletonów.
DGM
17
@DGM: Dokładnie - w rzeczywistości istnieje ogromna logiczna rozbieżność między „racjonalną” częścią artykułu a częścią „Singletony są przyczyną”.
Harper Shelby
1
Artykuł karty kredytowej Misko jest skrajnym przykładem niewłaściwego użycia tego wzorca.
Alex
2
Czytanie drugiego artykułu Singletony są właściwie częścią opisanego modelu obiektowego. Jednak zamiast być globalnie dostępne, są prywatne dla obiektów Factory.
FruitBreak
121

Myślę, że zamieszanie jest spowodowane tym, że ludzie nie znają prawdziwego zastosowania wzoru Singleton. Nie mogę tego wystarczająco podkreślić. Singleton nie jest wzorem do zawijania globali. Wzorca singletonu należy używać tylko w celu zagwarantowania, że ​​w czasie wykonywania istnieje jedno i tylko jedno wystąpienie danej klasy .

Ludzie myślą, że Singleton jest zły, ponieważ używają go do globalizacji. To z powodu tego zamieszania patrzy się na Singletona z góry. Proszę nie mylić singletonów i globałów. Jeśli zostanie użyty zgodnie z przeznaczeniem, uzyskasz ekstremalne korzyści ze wzoru Singleton.

Cem Catikkas
źródło
20
Co to za jedna instancja dostępna w aplikacji? O. Global . „Singleton nie jest wzorem do zawijania globali” jest albo naiwny, albo wprowadzający w błąd, ponieważ z definicji wzór otacza klasę wokół globalnego wystąpienia.
cHao
26
Oczywiście możesz swobodnie utworzyć jedną instancję klasy podczas uruchamiania aplikacji i wstrzyknąć tę instancję poprzez interfejs do wszystkiego, co z niej korzysta. Wdrożenie nie powinno obchodzić, że może być tylko jeden.
Scott Whitlock
4
@Dainius: W końcu tak naprawdę nie ma. Jasne, nie możesz arbitralnie zastąpić instancji inną. (Z wyjątkiem tych momentów wywołujących mimikę twarzy, kiedy to robisz . Właściwie widziałem już setInstancemetody.) To jednak nie ma znaczenia - to, że zaprzeczamy, że „potrzebny” singleton również nie wiedział dziwnej rzeczy o enkapsulacji lub co jest nie tak z zmienny stan globalny, więc pomocnie (?) zapewnił seterów dla każdego. pojedynczy. pole. (I tak, zdarza się. Bardzo często . Niemal każdy singleton, jaki kiedykolwiek widziałem na wolności, był zmienny z założenia i często zawstydzający.)
cHao
4
@Dainius: W dużym stopniu już to zrobiliśmy. „Wolę kompozycję niż dziedziczenie” było już od dłuższego czasu. Kiedy jednak dziedziczenie jest zdecydowanie najlepszym rozwiązaniem, możesz oczywiście z niego korzystać. To samo dotyczy singletonów, globałów, wątków gotoitp. Mogą one działać w wielu przypadkach, ale szczerze mówiąc, „działa” nie wystarczy - jeśli chcesz przeciwstawić się konwencjonalnej mądrości, lepiej być w stanie zademonstrować swoje podejście jest lepszy niż konwencjonalne rozwiązania. I jeszcze nie widziałem takiego przypadku dla wzoru Singleton.
cHao
3
Abyśmy nie rozmawiali obok siebie, nie mówię tylko o instancji dostępnej na całym świecie. Jest na to mnóstwo przypadków. To, o czym mówię (szczególnie, gdy mówię „Capital-S Singleton”), to wzór Singletona GoF, który osadza tę pojedynczą globalną instancję w samej klasie, ujawnia ją za pomocą getInstancemetody o podobnej nazwie lub zapobiega temu druga instancja. Szczerze mówiąc, w tym momencie równie dobrze możesz nie mieć jednego wystąpienia.
cHao
72

Jedną złą rzeczą związaną z singletonami jest to, że nie można ich bardzo łatwo przedłużyć. Zasadniczo musisz zbudować jakiś wzór dekoratora lub coś takiego, jeśli chcesz zmienić ich zachowanie. Ponadto, jeśli pewnego dnia chcesz mieć wiele sposobów na zrobienie tego, jedna rzecz, zmiana może być dość bolesna, w zależności od sposobu ułożenia kodu.

Należy zauważyć, że jeśli używasz singletonów, spróbuj przekazać je komukolwiek, kto ich potrzebuje, zamiast mieć bezpośredni dostęp do nich ... W przeciwnym razie, jeśli zdecydujesz się na wiele sposobów robienia tego, co robi singleton, będzie to raczej trudna do zmiany, ponieważ każda klasa zawiera zależność, jeśli uzyskuje bezpośredni dostęp do singletonu.

Więc w zasadzie:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

zamiast:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

Uważam, że tego rodzaju schemat nazywa się wstrzykiwaniem zależności i ogólnie uważa się go za dobrą rzecz.

Jak każdy wzór ... Pomyśl o tym i zastanów się, czy jego użycie w danej sytuacji jest niewłaściwe, czy nie ... Reguły są zwykle łamane, a wzorców nie należy bezmyślnie stosować.

Mike Stone
źródło
17
Heh, jeśli robicie to wszędzie, musielibyście wszędzie przekazywać odniesienia do singeltonów, a zatem nie macie już singletona. :) (Co zwykle jest dobrą rzeczą IMO.)
Bjarke Freund-Hansen
2
@ BjarkeFreund-Hansen - Bzdury, o czym ty mówisz? Singleton jest po prostu instancją klasy, która jest raz wdrożona. Odwołanie się do takiego Singletona nie kopiuje rzeczywistego obiektu, po prostu się do niego odwołuje - nadal masz ten sam obiekt (czytaj: Singleton).
M. Mimpen,
2
@ M.Mimpen: Nie, wielka litera Singleton (o czym tutaj dyskutujemy) jest instancją klasy, która (a) gwarantuje, że tylko jedna instancja kiedykolwiek będzie istniała, i (b) jest dostępna poprzez klasę własną wbudowany globalny punkt dostępu. Jeśli zadeklarowałeś, że nic nie powinno wołać getInstance(), to (b) nie jest już do końca prawdą.
cHao
2
@ cHao Nie podążam za tobą lub nie rozumiesz do kogo komentuję - czyli do Bjarke Freund-Hansen. Bjarke stwierdza, że ​​posiadanie kilku odniesień do singletonu powoduje, że ma kilka singletonów. Z pewnością nie jest to prawdą, ponieważ nie ma głębokich kopii.
M. Mimpen,
6
@ M.Mimpen: Biorę jego komentarz, aby bardziej odnieść się do efektu semantycznego. Po zakazaniu połączeń getInstance()skutecznie odrzuciłeś jedną użyteczną różnicę między wzorem Singleton a zwykłym odniesieniem. Jeśli chodzi o resztę kodu, pojedynczość nie jest już własnością. Tylko osoba dzwoniąca getInstance()musi wiedzieć, a nawet dbać o liczbę wystąpień. Tylko jeden dzwoniący kosztuje więcej wysiłku i elastyczności, aby rzetelnie egzekwować jednolitość, niż przechowywanie po prostu numeru referencyjnego i ponowne użycie go.
cHao
69

Wzorzec singletonu sam w sobie nie stanowi problemu. Problem polega na tym, że wzorzec jest często używany przez osoby opracowujące oprogramowanie z narzędziami obiektowymi, nie posiadające solidnego zrozumienia koncepcji OO. Kiedy singletony są wprowadzane w tym kontekście, stają się one klasami niemożliwymi do zarządzania, które zawierają metody pomocnicze dla każdego małego zastosowania.

Singletony są również problemem z punktu widzenia testowania. Zwykle utrudniają pisanie pojedynczych testów jednostkowych. Inwersja kontroli (IoC) i wstrzykiwanie zależności są wzorcami mającymi na celu przezwyciężenie tego problemu w sposób obiektowy, który nadaje się do testowania jednostkowego.

W środowisku śmieciowym singletony mogą szybko stać się problemem w zakresie zarządzania pamięcią.

Istnieje również scenariusz wielowątkowy, w którym singletony mogą stać się wąskim gardłem, a także problemem synchronizacji.

Kimoz
źródło
4
wiem, że to wieloletni wątek. Cześć @Kimoz powiedział: - singletony mogą szybko stać się problemem w zakresie zarządzania pamięcią. chciałby wyjaśnić bardziej szczegółowo, jaki problem może pojawić się w związku z singletonem i wyrzucaniem elementów bezużytecznych.
Thomas
@Kimoz, pytanie brzmi „Dlaczego wzorzec singletonu sam w sobie nie stanowi problemu?” a ty tylko powtórzyłeś punkt, ale nie podałeś ani jednego ważnego przypadku użycia wzorca singletonu.
Pacerier
@ Thomas, ponieważ singleton z definicji istnieje tylko w jednej instancji. Często przypisywanie jedynego odwołania do wartości null jest skomplikowane. Można to zrobić, ale oznacza to, że w pełni kontrolujesz punkt, po którym singleton nie jest używany w Twojej aplikacji. Jest to rzadkie i zwykle wręcz przeciwne do tego, czego szukają entuzjaści singletonów: prosty sposób, aby jedna instancja była zawsze dostępna. W niektórych ramach DI, takich jak Guice lub Dagger, nie można pozbyć się singletona i pozostaje on na zawsze w pamięci. (Choć pojedyncze pojemniki pod warunkiem, są znacznie lepsze niż domowe).
Snicolas
54

Singleton jest implementowany przy użyciu metody statycznej. Ludzie, którzy przeprowadzają testy jednostkowe, unikają metod statycznych, ponieważ nie można ich wyśmiewać ani karczować. Większość osób na tej stronie jest wielkimi zwolennikami testów jednostkowych. Generalnie najbardziej akceptowaną konwencją, aby ich uniknąć, jest stosowanie odwrócenia wzorca kontroli .

Peter Mortensen
źródło
9
To brzmi bardziej jak problem z testowaniem jednostek, które mogą testować obiekty (jednostki), funkcje (jednostki), całe biblioteki (jednostki), ale nie działają na niczym statycznym w klasie (także jednostkach).
v010dya
2
czy i tak nie przypuszczasz, że mocujesz wszystkie odniesienia zewnętrzne? Jeśli tak, to jaki jest problem z moc singletonem, jeśli nie, czy naprawdę przeprowadzasz testy jednostkowe?
Dainius
@Dainius: Wyśmiewanie instancji to o wiele mniej kłopotów niż wyśmiewanie klas. Można wyodrębnić testowaną klasę z pozostałej części aplikacji i przetestować z fałszywą Singletonklasą. To jednak ogromnie komplikuje proces testowania. Po pierwsze, teraz musisz mieć możliwość zwolnienia klas do woli (tak naprawdę nie jest to opcja w większości języków) lub uruchomienia nowej maszyny wirtualnej dla każdego testu (czytaj: testy mogą trwać tysiące razy). Jednak dla dwóch osób zależność Singletonjest szczegółem implementacji, który przecieka przez wszystkie testy.
cHao
Powermock może kpić z przedmiotów statycznych.
Snicolas
czy wyśmiewanie obiektu oznacza tworzenie prawdziwego obiektu? Jeśli wyśmiewanie nie tworzy prawdziwego obiektu, dlaczego ma to znaczenie, czy klasa jest singletonem, czy metoda jest statyczna?
Arun Raaj,
45

Singletony są również złe, jeśli chodzi o klastrowanie . Ponieważ wtedy nie ma już „dokładnie jednego singletonu” w aplikacji.

Rozważ następującą sytuację: Jako programista musisz utworzyć aplikację internetową, która uzyskuje dostęp do bazy danych. Aby upewnić się, że równoczesne wywołania bazy danych nie powodują konfliktów, należy utworzyć zapis wątków SingletonDao:

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

Jesteś więc pewien, że w aplikacji istnieje tylko jeden singleton, a cała baza danych przechodzi przez ten jeden i tylko jeden SingletonDao. Twoje środowisko produkcyjne wygląda teraz tak: Singleton

Jak dotąd wszystko jest w porządku.

Teraz rozważ, czy chcesz skonfigurować wiele instancji swojej aplikacji internetowej w klastrze. Teraz nagle masz coś takiego:

Wiele singletonów

Brzmi dziwnie, ale teraz masz wiele singletonów w swojej aplikacji . I właśnie tym nie powinien być singleton: posiadanie wielu obiektów. Jest to szczególnie złe, jeśli, jak pokazano w tym przykładzie, chcesz wykonywać zsynchronizowane połączenia z bazą danych.

Oczywiście jest to przykład złego użycia singletonu. Ale przesłanie tego przykładu jest następujące: Nie można polegać na tym, że w aplikacji jest dokładnie jedno wystąpienie singletonu - szczególnie jeśli chodzi o klastrowanie.

Uooo
źródło
4
jeśli nie wiesz, jak wdrożyć singleton, nie powinieneś tego robić. Jeśli nie wiesz, co robisz, powinieneś najpierw to znaleźć, a dopiero potem zrób to, czego potrzebujesz.
Dainius
3
To interesujące, mam pytanie. Więc na czym dokładnie polega problem, jeśli singletony - każdy na innym komputerze / maszynie JVM - łączą się z jedną bazą danych? Zasięg singletonów dotyczy tylko tej konkretnej maszyny JVM i jest to nadal prawdą nawet w klastrze. Zamiast po prostu filozoficznie powiedzieć, że ta szczególna sytuacja jest zła, ponieważ naszym celem był jeden obiekt w całej aplikacji, chętnie zobaczę wszelkie problemy techniczne, które mogą powstać z powodu tego ustawienia.
Saurabh Patil,
39
  1. Jest łatwo (ab) używany jako zmienna globalna.
  2. Klasy zależne od singletonów są stosunkowo trudniejsze do samodzielnego przetestowania jednostkowego.
jop
źródło
33

Monopol to diabeł, a singletony ze stanem nieczytelnym / zmiennym są „prawdziwym” problemem ...

Po przeczytaniu Singletonów są Patologicznymi Kłamcami, jak zasugerowano w odpowiedzi Jasona, natknąłem się na ten mały smakołyk, który stanowi najlepiej przedstawiony przykład tego, jak singletony są często niewłaściwie wykorzystywane.

Globalny jest zły, ponieważ:

  • za. Powoduje konflikt przestrzeni nazw
  • b. Naraża państwo w nieuzasadniony sposób

Jeśli chodzi o Singletony

  • za. Wyraźny sposób ich wywoływania przez OO zapobiega konfliktom, więc wskaż a. nie jest problemem
  • b. Singletony bez stanu (jak fabryki) nie stanowią problemu. Singletony ze stanem mogą ponownie należeć do dwóch kategorii, które są niezmienne lub piszą raz i czytają wiele (pliki konfiguracji / właściwości). To nie są złe. Zmienne singletony, które są rodzajem posiadaczy referencji, są tymi, o których mówisz.

W ostatnim oświadczeniu nawiązuje do koncepcji blogu „singletony są kłamcami”.

Jak to się ma do Monopolu?

Aby rozpocząć grę w monopol, najpierw:

  • najpierw ustalamy reguły, aby wszyscy byli na tej samej stronie
  • wszyscy mają równy start na początku gry
  • przedstawiono tylko jeden zestaw reguł, aby uniknąć nieporozumień
  • zasady nie mogą się zmieniać w trakcie gry

Dla każdego, kto tak naprawdę nie grał w monopol, standardy te są w najlepszym wypadku idealne. Porażka w monopolu jest trudna do przełknięcia, ponieważ w monopolu chodzi o pieniądze, jeśli przegrasz, musisz uważnie obserwować, jak reszta graczy kończy grę, a straty są zwykle szybkie i miażdżące. W związku z tym reguły zwykle ulegają zmianie w pewnym momencie, aby służyć interesom niektórych graczy kosztem innych.

Więc grasz w monopol z przyjaciółmi Bobem, Joe i Edem. Szybko budujesz swoje imperium i pochłaniasz udział w rynku w tempie wykładniczym. Twoi przeciwnicy słabną i zaczynasz wąchać krew (w przenośni). Twój kumpel Bob wkłada wszystkie swoje pieniądze w blokowanie jak największej liczby nieruchomości o niskiej wartości, ale nie osiąga wysokiego zwrotu z inwestycji w sposób, jakiego oczekiwał. Bob, jako powód pecha, ląduje na Boardwalk i zostaje usunięty z gry.

Teraz gra zmienia się z przyjaznego rzucania kostkami w poważny biznes. Bob został przykładem porażki, a Joe i Ed nie chcą skończyć jak „ten facet”. Będąc wiodącym graczem, nagle stajesz się wrogiem. Joe i Ed zaczynają ćwiczyć transakcje pod stołem, zastrzyki gotówki, niedowartościowane zamiany domów i ogólnie wszystko, co osłabi cię jako gracza, dopóki jeden z nich nie awansuje na szczyt.

Następnie, zamiast wygrania jednego z nich, proces zaczyna się od nowa. Nagle skończony zestaw zasad staje się ruchomym celem, a gra przeradza się w rodzaj interakcji społecznych, które stanowiłyby podstawę każdego wysoko ocenianego reality show od czasu Survivor. Dlaczego, ponieważ zasady się zmieniają i nie ma zgody co do tego, jak / dlaczego / co mają reprezentować, a co ważniejsze, nikt nie podejmuje decyzji. W tym momencie każdy gracz w grze ustanawia własne zasady i nastaje chaos, dopóki dwóch graczy nie będzie zbyt zmęczonych, aby nadążyć za szaradą i powoli się poddać.

Tak więc, jeśli zbiór reguł gry dokładnie reprezentowałby singletona, to zbiór reguł monopolu byłby przykładem nadużycia.

Jak to się ma do programowania?

Oprócz wszystkich oczywistych problemów związanych z bezpieczeństwem wątków i synchronizacją, które występują w mutowalnych singletonach ... Jeśli masz jeden zestaw danych, który może być odczytywany / manipulowany przez wiele różnych źródeł jednocześnie i istnieje przez cały czas wykonywania aplikacji, prawdopodobnie jest to dobry moment, aby cofnąć się i zapytać „czy używam tutaj odpowiedniej struktury danych”.

Osobiście widziałem, jak programiści nadużywają singletonu, używając go jako pewnego rodzaju skręconego sklepu z bazami danych w aplikacji. Pracując bezpośrednio nad kodem, mogę zaświadczyć, że był on powolny (z powodu wszystkich blokad wątków potrzebnych do zapewnienia bezpieczeństwa wątków) i koszmarem do pracy (z powodu nieprzewidywalnej / przerywanej natury błędów synchronizacji) oraz prawie niemożliwe do przetestowania w warunkach „produkcyjnych”. Jasne, można by opracować system wykorzystujący odpytywanie / sygnalizację w celu przezwyciężenia niektórych problemów z wydajnością, ale to nie rozwiązałoby problemów z testowaniem i po co zawracać sobie głowę tym, kiedy „prawdziwa” baza danych może już osiągnąć tę samą funkcjonalność w znacznie bardziej solidnej / sposób skalowalny.

Singleton jest opcją tylko , jeśli potrzebujesz tego, co zapewnia singleton. Wystąpienie obiektu tylko do odczytu dla obiektu. Ta sama reguła powinna również kaskadować do właściwości / elementów obiektu.

Evan Plaice
źródło
1
Co jeśli singleton zamieni niektóre dane?
Yola,
@Yola Zmniejszyłoby to liczbę zapisów, gdyby singleton był aktualizowany zgodnie z ustalonym i / lub ustalonym harmonogramem, zmniejszając w ten sposób rywalizację wątków. Mimo to nie będziesz w stanie dokładnie przetestować żadnego kodu wchodzącego w interakcję z singletonem, chyba że wyśmiejesz instancję inną niż singleton, która symuluje to samo użycie. Ludzie TDD prawdopodobnie mieliby atak, ale to by działało.
Evan Plaice,
@EvanPlaice: Nawet z próbą masz problemy z kodem, który mówi Singleton.getInstance(). Język obsługujący odbicie może być w stanie obejść ten problem, ustawiając pole, w którym przechowywana jest jedna prawdziwa instancja. IMO jednak testy stają się nieco mniej wiarygodne, gdy zaczniesz ćwiczyć z prywatnym stanem innej klasy.
cHao
28

Singleton nie dotyczy pojedynczego wystąpienia!

W przeciwieństwie do innych odpowiedzi, nie chcę rozmawiać o tym, co jest nie tak z Singletonami, ale pokazać wam, jak potężne i niesamowite są, gdy są właściwie stosowane!

  • Problem : Singleton może stanowić wyzwanie w środowisku wielowątkowym
    Rozwiązanie : Użyj procesu jednowątkowego ładowania początkowego, aby zainicjować wszystkie zależności twojego singletona.
  • Problem : Trudno kpić z singletonów.
    Rozwiązanie : Użyj metody Fabryczny wzór do kpienia

Możesz odwzorować MyModelna TestMyModeldziedziczącą klasę, wszędzie tam, gdzie MyModelzostanie wstrzyknięty, pojawi się TestMyModelinstread. - Problem : Singletony mogą powodować wycieki pamięci, ponieważ nigdy nie zostały usunięte.
Rozwiązanie : Pozbądź się ich! Zaimplementuj funkcję zwrotną w swojej aplikacji, aby poprawnie pozbyć się singletonów, powinieneś usunąć wszelkie powiązane z nimi dane i wreszcie: usunąć je z fabryki.

Jak wspomniałem w tytule singleton nie dotyczy pojedynczego wystąpienia.

  • Singletony poprawiają czytelność : możesz spojrzeć na swoją klasę i zobaczyć, jaki singleton wstrzyknął, aby dowiedzieć się, jakie są zależności.
  • Singletony poprawia konserwację : po usunięciu zależności z klasy usunąłeś tylko zastrzyk singletonu, nie musisz iść i edytować dużego linku innych klas, które właśnie przeniosły twoją zależność (to śmierdzący kod dla mnie @Jim Burger )
  • Singletony poprawiają pamięć i wydajność : gdy coś dzieje się w Twojej aplikacji i wymaga długiego łańcucha zwrotów, marnujesz pamięć i wydajność, używając Singleton odcinasz pośrednika, a także poprawiasz wydajność i zużycie pamięci ( przez unikanie niepotrzebnego przydzielania zmiennych lokalnych).
Ilya Gazman
źródło
2
Nie rozwiązuje to mojego głównego problemu z Singletonami, który umożliwia dostęp do stanu globalnego z dowolnej z tysięcy klas w twoim projekcie.
LegendLength
8
To byłby cel ...
Ilya Gazman
LegendLength Dlaczego to źle? Na przykład w mojej aplikacji mam Settingsobiekt singleton, który jest dostępny z dowolnego widgetu, dzięki czemu każdy widget wie, jak sformatować wyświetlane liczby. Gdyby nie był to obiekt dostępny na całym świecie, musiałbym wstrzyknąć go do konstruktora każdemu widgetowi w konstruktorze i zachować odniesienie do niego jako zmiennej składowej. To straszne marnowanie pamięci i czasu.
VK
23

Moja odpowiedź na temat tego, jak Singletonowie są źli, zawsze brzmi: „ciężko jest zrobić dobrze”. Wiele podstawowych składników języków to singletony (klasy, funkcje, przestrzenie nazw, a nawet operatory), podobnie jak komponenty w innych aspektach obliczeń (host lokalny, domyślna trasa, wirtualny system plików itp.) I nie jest to przypadek. Chociaż od czasu do czasu powodują kłopoty i frustrację, mogą również sprawić, że wiele rzeczy działa dużo lepiej.

Dwa największe błędy, które widzę to: traktowanie tego jak globalne i brak definicji zamknięcia singletonu.

Wszyscy mówią o Singletonach jako o globalsach, bo tak po prostu są. Jednak znaczna część (niestety nie wszystkie) zła w globalności nie wynika z samej bycia globalną, ale z tego, jak z niej korzystasz. To samo dotyczy singletonów. W rzeczywistości tym bardziej, że „pojedyncze wystąpienie” tak naprawdę nie musi oznaczać „globalnie dostępny”. Jest to bardziej naturalny produkt uboczny, a biorąc pod uwagę wszystkie znane nam zło, nie powinniśmy spieszyć się z wykorzystaniem globalnej dostępności. Gdy programiści zobaczą Singletona, wydają się zawsze uzyskiwać do niego bezpośredni dostęp za pomocą metody instancji. Zamiast tego powinieneś nawigować do niego tak, jak do każdego innego obiektu. Większość kodu nie powinna nawet zdawać sobie sprawy, że ma do czynienia z Singletonem (luźne sprzężenie, prawda?). Jeśli tylko niewielka część kodu uzyskuje dostęp do obiektu, tak jakby był globalny, wiele szkód zostaje cofniętych.

Bardzo ważny jest również kontekst Singleton. Cechą charakterystyczną Singletona jest to, że istnieje „tylko jeden”, ale prawda jest taka, że ​​jest „tylko jeden” w jakimś kontekście / przestrzeni nazw. Zazwyczaj są to: jeden na wątek, proces, adres IP lub klaster, ale może być także jeden na procesor, maszynę, przestrzeń nazw języka / moduł ładujący klasy / cokolwiek, podsieć, Internet itp.

Innym, mniej powszechnym błędem jest ignorowanie stylu życia Singletona. To, że istnieje tylko jeden, nie oznacza, że ​​Singleton jest jakimś wszechmocnym „zawsze był i zawsze będzie”, ani też nie jest ogólnie pożądany (obiekty bez początku i końca naruszają wszelkiego rodzaju użyteczne założenia w kodzie i powinny być stosowane tylko w najbardziej desperackich okolicznościach.

Jeśli unikniesz tych błędów, Singletony nadal mogą być PITA, ale jest gotowy, aby zobaczyć, że wiele najgorszych problemów zostanie znacznie złagodzonych. Wyobraź sobie Java Singleton, który jest jednoznacznie zdefiniowany jako jeden raz na moduł ładujący klasę (co oznacza, że ​​potrzebuje polityki bezpieczeństwa wątków), ze zdefiniowanymi metodami tworzenia i niszczenia oraz cyklem życia, który określa, kiedy i jak mają być wywoływane, i którego metoda „instancji” ma ochrona pakietu, więc jest ogólnie dostępna za pośrednictwem innych, nieglobalnych obiektów. Nadal potencjalne źródło problemów, ale z pewnością znacznie mniej problemów.

Niestety, zamiast uczyć dobrych przykładów robienia singletonów. Uczymy złych przykładów, pozwalamy programistom na chwilę z nich korzystać, a następnie informujemy, że są złym wzorcem projektowym.

Christopher Smith
źródło
23

Zobacz Wikipedia Singleton_pattern

Jest również uważany za anty-wzorzec przez niektórych ludzi, którzy uważają, że jest on nadmiernie wykorzystywany, wprowadzając niepotrzebne ograniczenia w sytuacjach, w których nie jest w rzeczywistości wymagana jedna instancja klasy. [1] [2] [3] [4]

Referencje (tylko istotne odniesienia z artykułu)

  1. ^ Alex Miller. Wzory, których nienawidzę # 1: Singleton , lipiec 2007
  2. ^ Scott Densmore. Dlaczego singletony są złe , maj 2004
  3. ^ Steve Yegge. Singletons za głupie , wrzesień 2004
  4. ^ JB Rainsberger, IBM. Mądrze korzystaj z singletonów , lipiec 2001
GUI Junkie
źródło
8
Opis wzoru nie wyjaśnia, dlaczego uważa się go za zło ...
Jrgns,
2
Raczej niesprawiedliwe: „Uważany jest również za anty-wzorzec przez niektórych ludzi, którzy uważają, że jest on zbyt często stosowany, wprowadzając niepotrzebne ograniczenia w sytuacjach, w których nie jest wymagana jedna instancja klasy”. Spójrz na referencje ... W każdym razie jest dla mnie RTFM.
GUI Junkie
17

To nie jest tak, że same singletony są złe, ale wzór projektu GoF jest. Jedynym naprawdę ważnym argumentem jest to, że wzorzec projektowy GoF nie nadaje się do testowania, zwłaszcza jeśli testy są uruchamiane równolegle.

Użycie pojedynczej instancji klasy jest prawidłową konstrukcją, o ile zastosujesz w kodzie następujące środki:

  1. Upewnij się, że klasa, która będzie używana jako singleton, implementuje interfejs. Umożliwia to implementację kodów pośredniczących lub próbnych przy użyciu tego samego interfejsu

  2. Upewnij się, że Singleton jest bezpieczny dla wątków. To jest dane.

  3. Singleton powinien być z natury prosty i niezbyt skomplikowany.

  4. Podczas działania aplikacji, w której singletony muszą zostać przekazane do danego obiektu, użyj fabryki klas, która buduje ten obiekt, i pozwól, aby fabryka klas przekazała instancję singletonu do klasy, która tego potrzebuje.

  5. Podczas testowania i w celu zapewnienia deterministycznego zachowania utwórz klasę singleton jako osobną instancję jako samą klasę lub stub / mock, który implementuje swoje zachowanie i przekazuje ją niezmienionej klasie, która tego wymaga. Nie używaj współczynnika klasy, który tworzy testowany obiekt, który potrzebuje singletonu podczas testu, ponieważ przejdzie on przez jedną globalną instancję tego obiektu, która nie spełnia celu.

W naszych rozwiązaniach wykorzystaliśmy Singletony z dużym sukcesem, który można przetestować, zapewniając deterministyczne zachowanie w równoległych strumieniach testowych.

użytkownik1578119
źródło
+1, w końcu odpowiedź, która określa, kiedy singleton może być ważny.
Pacerier
16

Chciałbym odnieść się do 4 punktów w przyjętej odpowiedzi, mam nadzieję, że ktoś może wyjaśnić, dlaczego się mylę.

  1. Dlaczego ukrywanie zależności w kodzie jest złe? Istnieją już dziesiątki ukrytych zależności (wywołania środowiska wykonawczego C, wywołania interfejsu API systemu operacyjnego, wywołania funkcji globalnych), a zależności singletonu są łatwe do znalezienia (wyszukaj na przykład ()).

    „Tworzenie czegoś globalnego, aby uniknąć przekazywania go, to zapach kodu”. Dlaczego nie podaje się czegoś, by nie sprawić, by singleton miał zapach kodu?

    Jeśli przepuszczasz obiekt przez 10 funkcji w stosie wywołań tylko po to, aby uniknąć singletonu, czy to takie wspaniałe?

  2. Zasada pojedynczej odpowiedzialności: Myślę, że jest to nieco niejasne i zależy od twojej definicji odpowiedzialności. Istotne byłoby pytanie, dlaczego dodanie tej konkretnej „odpowiedzialności” do sprawy klasowej?

  3. Dlaczego przekazanie obiektu do klasy powoduje, że jest on ściślej sprzężony niż używanie tego obiektu jako singletonu z klasy?

  4. Dlaczego zmienia to, jak długo trwa stan? Singletony mogą być tworzone lub niszczone ręcznie, więc kontrola jest nadal dostępna i możesz ustawić czas życia taki sam, jak trwałość obiektu nie będącego singletonem.

Odnośnie testów jednostkowych:

  • nie wszystkie klasy muszą być testowane jednostkowo
  • nie wszystkie klasy, które muszą być testowane jednostkowo, muszą zmienić implementację singletonu
  • gdyby zrobić musi być testowane urządzenie i trzeba zmieniać wdrożenia, łatwo zmienić klasę z użyciem pojedyncza do posiadające singleton przekazany do niego poprzez wstrzyknięcie zależności.
Jon
źródło
1
1. Wszystkie te ukryte zależności? Te też są złe. Ukryte zależności są zawsze złe. Ale w przypadku CRT i systemu operacyjnego są już dostępne i są jedynym sposobem, aby coś zrobić. (Spróbuj napisać program C bez użycia środowiska uruchomieniowego lub systemu operacyjnego.) Ta zdolność do robienia rzeczy znacznie przewyższa ich negatywne strony. Singletony w naszym kodzie nie mają tego luksusu; ponieważ są one ściśle objęte naszą kontrolą i odpowiedzialnością, każde użycie (czytaj: każda dodatkowa ukryta zależność) powinno być uzasadnione jako jedyny rozsądny sposób na załatwienie sprawy. Bardzo, bardzo niewielu z nich rzeczywiście są.
cHao
2. „Odpowiedzialność” jest zazwyczaj definiowana jako „powód do zmiany”. Singleton kończy zarówno zarządzanie swoim życiem, jak i wykonywanie swojej prawdziwej pracy. Jeśli zmienisz sposób wykonywania zadania lub budowy wykresu obiektowego, musisz zmienić klasę. Ale jeśli masz inny kod do zbudowania wykresu obiektowego, a twoja klasa po prostu wykonuje swoją prawdziwą robotę, ten kod inicjujący może skonfigurować wykres obiektowy, jak tylko chce. Wszystko jest o wiele bardziej modułowe i testowalne, ponieważ możesz wstawiać podwójne testowe i niszczyć wszystko do woli, a nie polegasz już na ukrytych ruchomych częściach.
cHao
1
@ cHao: 1. Dobrze, że zdajesz sobie sprawę, że „zdolność do robienia rzeczy znacznie przewyższa ich negatywne strony”. Przykładem może być struktura rejestrowania. Nakłanianie do przekazywania globalnego obiektu rejestrowania przez wstrzykiwanie zależności przez warstwy wywołań funkcji jest złem, jeśli oznacza to, że programiści są zniechęcani do rejestrowania. Dlatego singleton. 3. Twój ciasny punkt sprzężenia jest istotny tylko wtedy, gdy chcesz, aby klienci tej klasy kontrolowali zachowanie poprzez polimorfizm. Często tak nie jest.
Jon
2
4. Nie jestem pewien, czy „nie można zniszczyć ręcznie” jest logiczną implikacją „może być tylko jedna instancja”. Jeśli tak definiujesz singleton, to nie mówimy o tym samym. Wszystkie klasy nie powinny być testowane jednostkowo. Jest to decyzja biznesowa, a czasem czas na wprowadzenie na rynek lub rozwój ma pierwszeństwo.
Jon
1
3. Jeśli nie chcesz polimorfizmu, nie potrzebujesz nawet instancji. Równie dobrze możesz uczynić go klasą statyczną, ponieważ i tak przywiązujesz się do jednego obiektu i pojedynczej implementacji - a przynajmniej wtedy API nie tyle cię okłamuje.
cHao
15

Vince Huston ma następujące kryteria, które wydają mi się rozsądne:

Singleton należy rozważyć tylko wtedy, gdy wszystkie trzy z następujących kryteriów są spełnione:

  • Nie można racjonalnie przypisać prawa własności do pojedynczej instancji
  • Pożądana jest leniwa inicjalizacja
  • Globalny dostęp nie jest inaczej przewidziany

Jeśli własność pojedynczej instancji, czas i sposób inicjalizacji oraz globalny dostęp nie stanowią problemu, Singleton nie jest wystarczająco interesujący.

js.
źródło
14

Singletony są złe z purystycznego punktu widzenia.

Z praktycznego punktu widzenia singleton jest kompromisem między czasem rozwoju a złożonością .

Jeśli wiesz, że twoja aplikacja nie zmieni się aż tak bardzo, jest całkiem w porządku. Po prostu wiedz, że może być konieczne przefakturowanie rzeczy, jeśli twoje wymagania zmienią się w nieoczekiwany sposób (co w większości przypadków jest całkiem OK).

Singletony czasami komplikują również testy jednostkowe .

tacone
źródło
3
Z mojego doświadczenia wynika, że rozpoczęcie od singletonów naprawdę cię skrzywdzi, nawet w krótkim okresie, nie licząc długiego okresu. Ten efekt może mnie mniej zmniejszyć, jeśli aplikacja istnieje już od jakiegoś czasu i prawdopodobnie jest już zainfekowana innymi singletonami. Zaczynasz od zera? Unikaj ich za wszelką cenę! Chcesz pojedynczej instancji obiektu? Pozwól fabryce zarządzać instancją tego obiektu.
Sven
1
AFAIK Singleton jest metodą fabryczną. Dlatego nie otrzymuję twojej rekomendacji.
tacone
3
„A factory”! = „Factory_method, której nie można oddzielić od innych przydatnych rzeczy w tej samej klasie”
Sven
13

Wzorzec ten nie ma z natury nic złego, zakładając, że jest on używany do pewnego aspektu twojego modelu, który jest naprawdę pojedynczy.

Uważam, że luz jest spowodowany jego nadmiernym wykorzystaniem, które z kolei wynika z faktu, że jest to najłatwiejszy wzór do zrozumienia i wdrożenia.

Ben Hoffstein
źródło
6
Myślę, że luz ma więcej wspólnego z osobami ćwiczącymi sformalizowane testy jednostkowe, zdając sobie sprawę, że to koszmar, z którym trzeba sobie poradzić.
Jim Burger,
1
Nie jestem pewien, dlaczego ma to wartość -1. To nie jest najbardziej opisowa odpowiedź, ale też się nie myli.
Outlaw Programmer
13

Nie zamierzam komentować argumentu dobro / zło, ale nie użyłem ich od wiosny . Używanie wstrzykiwania zależności prawie całkowicie usunęło moje wymagania dotyczące singletonów, serwisantów i fabryk. Uważam, że jest to o wiele bardziej produktywne i czyste środowisko, przynajmniej dla rodzaju wykonywanej pracy (aplikacje internetowe oparte na Javie).

Peter Mortensen
źródło
3
czy fasola szparagowa nie jest domyślnie singletonem?
szczupły
3
Tak, ale mam na myśli „kodowanie” singletonów. Nie napisałem „singletona” (tj. Z prywatnym konstruktorem yada yada yada według wzoru) - ale tak, ze wiosną używam jednego wystąpienia.
prule
4
więc kiedy inni to robią, jest w porządku (jeśli użyję tego później), ale jeśli inni to zrobią i nie będę tego używał, to jest złe?
Dainius
11

Singleton jest wzorem i może być używany lub nadużywany tak jak każde inne narzędzie.

Złą częścią singletonu jest na ogół użytkownik (lub powinienem powiedzieć niewłaściwe użycie singletona do rzeczy, do których nie jest przeznaczony). Największy przestępca wykorzystuje singletona jako fałszywą zmienną globalną.

Loki Astari
źródło
1
Dzięki Loki :) Dokładnie o to mi chodzi. Całe polowanie na czarownice przypomina mi debatę goto. Narzędzia są dla osób, które wiedzą, jak z nich korzystać; korzystanie z narzędzia, którego nie lubisz, może być dla Ciebie niebezpieczne, więc unikaj go, ale NIE mów innym, aby nie używali / nie uczyli się go prawidłowo używać. Nie jestem „pro-singletonem”, ale podoba mi się to, że mam takie narzędzie i czuję, gdzie warto go używać. Kropka.
dkellner
8

Kiedy piszesz kod za pomocą singletonów, powiedzmy, programu rejestrującego lub połączenia z bazą danych, a potem okazuje się, że potrzebujesz więcej niż jednego dziennika lub więcej niż jednej bazy danych, masz kłopoty.

Singletony bardzo utrudniają przejście od nich do zwykłych przedmiotów.

Ponadto zbyt łatwo jest napisać singleton, który nie jest bezpieczny dla wątków.

Zamiast używać singletonów, należy przekazać wszystkie potrzebne obiekty narzędziowe z funkcji do funkcji. Można to uprościć, jeśli owiniesz je wszystkie w obiekt pomocnika, taki jak ten:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}
Roman Odaisky
źródło
4
przekazywanie argumentów dla każdej metody jest genialne, powinno przejść do co najmniej 10 najlepszych sposobów na zanieczyszczenie interfejsu API.
Dainius
Jest to oczywista wada, którą powinien zająć się język za pomocą argumentów, które w jakiś sposób rozprzestrzeniają się w wywołania zagnieżdżone, ale bez takiego wsparcia, należy to zrobić ręcznie. Weź pod uwagę, że nawet naturalne singletony, takie jak obiekt reprezentujący zegar systemowy, mogą mieć problemy. Na przykład, w jaki sposób zamierzasz objąć testami kod zależny od zegara (licznik licznika energii z taryfą wielokrotną)?
Roman Odaisky
zależy od tego, co testujesz, jeśli nie testujesz singletona, dlaczego zależy ci na jego zachowaniu, jeśli twój 2 zdalny obiekt zależy od siebie, to nie jest problem singletonu.
Dainius
Czy możesz podać, który język propagowałby do zagnieżdżonych wywołań?
Dainius
1
Jeśli masz jeden moduł, który zależy od innego i nie wiesz / nie możesz go wzmocnić, będziesz miał trudności niezależnie od tego, czy jest to singleton czy nie. Ludzie często używają dziedziczenia często, gdzie powinni używać kompozycji, ale nie mówicie, że dziedziczenie jest złe, a OOP należy unikać za wszelką cenę, ponieważ tak wiele osób popełnia błędy projektowe, a ty?
Dainius
8

Problemy z singletonami polegają na zwiększeniu zakresu, a zatem sprzężenia . Nie można zaprzeczyć, że istnieją sytuacje, w których potrzebujesz dostępu do jednej instancji, i można to zrobić na inne sposoby.

Teraz wolę projektować wokół kontenera z inwersją sterowania (IoC) i pozwalać na kontrolowanie żywotności kontenera. Daje to korzyści klasom zależnym od instancji, które nie są świadome faktu, że istnieje jedna instancja. Czas życia singletonu można zmienić w przyszłości. Kiedyś takim przykładem, z którym ostatnio się spotkałem, była łatwa regulacja od jednowątkowego do wielowątkowego.

FWIW, jeśli jest to PIA, gdy próbujesz ją przetestować, to przejdzie do PIA, gdy spróbujesz debugować, naprawić błąd lub go ulepszyć.

Mark Lindell
źródło
7

Singletony NIE są złe. Jest to złe tylko wtedy, gdy tworzysz coś globalnie wyjątkowego, co nie jest globalnie wyjątkowe.

Istnieją jednak „usługi zakresu aplikacji” (pomyśl o systemie przesyłania komunikatów, który umożliwia interakcję komponentów) - to WEZWANIE dla singletona, „MessageQueue” - klasa, która ma metodę „SendMessage (...)”.

Następnie możesz wykonać następujące czynności z całego miejsca:

MessageQueue.Current.SendMessage (new MailArrivedMessage (...));

I oczywiście:

MessageQueue.Current.RegisterReceiver (this);

w klasach, które implementują IMessageReceiver.

StormianRootSolver
źródło
6
A jeśli chcę utworzyć drugą kolejkę komunikatów o mniejszym zakresie, dlaczego nie miałbym pozwolić na ponowne użycie kodu do jej utworzenia? Singleton temu zapobiega. Ale jeśli właśnie utworzyłeś zwykłą klasę kolejki komunikatów, a następnie utworzyłeś jedną globalną instancję, która działałaby jako „zakres aplikacji”, mógłbym utworzyć drugą instancję do innego użytku. Ale jeśli uczynisz klasę singletonem, będę musiał napisać drugą klasę kolejki wiadomości.
lipiec
1
Dlaczego nie mielibyśmy mieć globalnej listy CustomerOrderList, abyśmy mogli ładnie ją nazywać z dowolnego miejsca, takiego jak MessageQueue? Uważam, że odpowiedź jest taka sama dla obu: skutecznie tworzy zmienną globalną.
LegendLength
7

Zbyt wiele osób umieszcza obiekty, które nie są bezpieczne dla wątków, według wzoru singletonu. Widziałem przykłady DataContext ( LINQ do SQL ) wykonane we wzorcu singletonowym, pomimo faktu, że DataContext nie jest bezpieczny dla wątków i jest obiektem czysto jednostkowym.

Peter Mortensen
źródło
wiele osób pisze niebezpieczny wielowątkowy kod, czy to oznacza, że ​​powinniśmy wyeliminować wątki?
Dainius
@Dainius: W rzeczywistości tak. IMO domyślnym modelem współbieżności powinien być proces wieloprocesowy, z możliwością, aby dwa procesy (1) łatwo przekazywały sobie komunikaty i / lub (2) współdzieliły pamięć w razie potrzeby. Wątki są przydatne, jeśli chcesz udostępniać wszystko , ale tak naprawdę nigdy tego nie chcesz. Można go emulować, dzieląc całą przestrzeń adresową, ale byłoby to również uważane za anty-wzorzec.
cHao
@Dainius: I oczywiście przy założeniu, że w ogóle potrzebna jest współbieżność między uczciwością a dobrocią. Często wszystko, czego chcesz, to móc zrobić jedną rzecz, czekając, aż system operacyjny zrobi coś innego. (Na przykład aktualizacja interfejsu użytkownika podczas odczytywania z gniazda). Przyzwoity asynchroniczny interfejs API może w większości przypadków nie wymagać pełnego wątkowania.
cHao
więc jeśli masz ogromną macierz danych, które potrzebujesz procesu, lepiej zrobić to w jednym wątku, ponieważ dla wielu osób może to zrobić źle.
Dainius
@Dainius: W wielu przypadkach tak. (Wątek może faktycznie spowolnić , jeśli dodasz synchronizację do miksu. Jest to doskonały przykład tego, dlaczego wielowątkowość nie powinna być pierwszą myślą większości ludzi.) W innych przypadkach lepiej byłoby mieć dwa procesy, które współużytkują tylko potrzebne rzeczy. Oczywiście trzeba będzie zorganizować procesy współużytkowania pamięci. Ale szczerze mówiąc, uważam to za dobrą rzecz - przynajmniej o wiele lepszą niż domyślny tryb współdzielenia wszystkiego. Wymaga to wyraźnego powiedzenia (a więc najlepiej, aby wiedzieć), które części są wspólne, a zatem które części muszą być bezpieczne dla wątków.
cHao
6

Oto jeszcze jedna rzecz o singletonach, o której nikt jeszcze nie powiedział.

W większości przypadków „singletonity” jest szczegółem implementacji dla niektórych klas, a nie cechą interfejsu. Odwrócenie kontenera sterującego może ukryć tę właściwość przed użytkownikami klasy; wystarczy zaznaczyć swoją klasę jako singleton ( @Singletonna przykład z adnotacją w Javie) i to wszystko; IoCC zajmie się resztą. Nie musisz zapewniać globalnego dostępu do instancji singleton, ponieważ dostęp jest już zarządzany przez IoCC. Dlatego nie ma nic złego w IoC Singletons.

GoF Singletons w przeciwieństwie do IoC Singletons mają ujawniać „singletonity” w interfejsie za pomocą metody getInstance (), tak aby cierpiały na wszystko, co powiedziano powyżej.

Włodzimierz Frolow
źródło
3
Moim zdaniem „singletonity” jest szczegółowym środowiskiem wykonawczym i nie powinien być brany pod uwagę przez programistę, który napisał kod klasy. Raczej rozważa to USER klasy. tylko UŻYTKOWNIK wie, ile instancji jest faktycznie wymaganych.
Earth Engine
5

Singletony nie są złe, jeśli używasz ich właściwie i minimalnie . Istnieje wiele innych dobrych wzorców projektowych, które w pewnym momencie zastępują potrzeby singletonu (i dają również najlepsze wyniki). Ale niektórzy programiści nie są świadomi tych dobrych wzorców i używają singletonu we wszystkich przypadkach, co czyni singletona złymi dla nich.

Sriram
źródło
Niesamowite! Całkowicie się zgadzam! Ale możesz, a może powinieneś rozwinąć o wiele więcej. Takich jak rozwijanie, które wzorce projektowe są najczęściej ignorowane i jak „właściwie i minimalnie” używać singletonów. Łatwiej powiedzieć niż zrobić ! : P
cregox
Zasadniczo alternatywą jest przekazywanie obiektów za pomocą parametrów metody, zamiast uzyskiwania do nich dostępu za pośrednictwem stanu globalnego.
LegendLength
5

Po pierwsze, klasa i jej współpracownicy powinni najpierw wykonać zamierzony cel, zamiast koncentrować się na osobach zależnych. Zarządzanie cyklem życia (gdy tworzone są instancje i kiedy wykraczają one poza zakres) nie powinny wchodzić w zakres odpowiedzialności klas. Zaakceptowaną najlepszą praktyką jest tworzenie lub konfigurowanie nowego komponentu do zarządzania zależnościami za pomocą wstrzykiwania zależności.

Często oprogramowanie staje się bardziej skomplikowane, dlatego sensowne jest posiadanie wielu niezależnych instancji klasy Singleton o różnych stanach. W takich przypadkach popełnianie kodu, aby po prostu chwycić singletona, jest błędne. Za pomocąSingleton.getInstance() może być odpowiednie dla małych prostych systemów, ale nie działa / nie skaluje się, gdy można potrzebować innej instancji tej samej klasy.

Żadna klasa nie powinna być traktowana jako singleton, ale raczej powinna być aplikacją, w której jest używana lub w jaki sposób służy do konfigurowania zależności. Dla szybkiego i nieprzyjemnego nie ma to znaczenia - tylko luźne kodowanie mówi, że ścieżki plików nie mają znaczenia, ale w przypadku większych aplikacji takie zależności muszą być uwzględnione i zarządzane w bardziej odpowiedni sposób za pomocą DI.

Problemy, które singleton powoduje w testach, są symptomem ich zakodowanego pojedynczego przypadku użycia / środowiska. Zestaw testowy i wiele testów są z osobna i oddzielają coś, co nie jest kompatybilne z kodowaniem pojedynczym singletona.

mP.
źródło
4

Ponieważ są to zasadniczo zmienne globalne zorientowane obiektowo, zazwyczaj można zaprojektować swoje klasy w taki sposób, aby nie były potrzebne.

Ycros
źródło
Jeśli twoja klasa nie jest ograniczona do jednej instancji, będziesz potrzebować statycznych członków w swojej klasie zarządzanych przez semafory, co daje prawie to samo! Jaka jest twoja proponowana alternatywa?
Jeach
Mam ogromną aplikację z „MainScreen”, ten ekran otwiera wiele mniejszych modalnych / niemodalnych formularzy systemu Windows / interfejsu użytkownika. IMHO Uważam, że MainScreen powinien być singletonem, więc na przykład widżet gdzieś w odległym rogu aplikacji chce pokazać swój status na pasku stanu MainScreen, wszystko co musi zrobić, to MainScreen.getInstance (). SetStatus ( "Jakiś tekst"); Co proponujesz jako alternatywę? Przekaż MainScreen całej aplikacji? : D
Salvin Francis
2
@SalvinFrancis: To, co polecam, to przestać mieć obiekty, które dbają o rzeczy, na których nie powinny się przejmować, i przekraść się po aplikacji, aby się ze sobą bawić. :) Twój przykład byłby znacznie lepszy w przypadku wydarzeń. Gdy poprawnie wykonujesz zdarzenia, widżet nie musi nawet dbać o to, czy w ogóle jest ekran główny; po prostu transmituje „hej, coś się stało”, a wszystko, co zasubskrybowało zdarzenie „coś się stało” (czy to MainScreen, WidgetTest, czy coś zupełnie innego!) decyduje, jak chce odpowiedzieć. Tak właśnie powinno być zrobione OOP . :)
cHao
1
@Salvin Zastanów się, jak trudno jest rozumować MainScreen podczas debugowania, jeśli jest on „dyskretnie” aktualizowany przez wiele komponentów. Twój przykład jest doskonałym powodem, dla którego singletony są złe.
LegendLength