Singletony są jak komunizm: oba brzmią świetnie na papierze, ale w praktyce wybuchają problemami.
Wzorzec singletonowy kładzie nieproporcjonalny nacisk na łatwość dostępu do obiektów. Całkowicie unika kontekstu, wymagając, aby każdy konsument używał obiektu o zasięgu AppDomain, nie pozostawiając żadnych opcji dla różnych implementacji. Osadza wiedzę o infrastrukturze w twoich zajęciach (wezwanie do GetInstance()
), dodając dokładnie zerową moc ekspresji. W rzeczywistości zmniejsza to twoją siłę ekspresji, ponieważ nie możesz zmienić implementacji używanej przez jedną klasę bez zmiany jej dla wszystkich . Po prostu nie możesz dodać jednorazowych elementów funkcjonalności.
Ponadto, gdy klasa Foo
zależy Logger.GetInstance()
, Foo
skutecznie ukrywa swoje zależności przed konsumentami. Oznacza to, że nie możesz go w pełni zrozumieć Foo
ani używać z pewnością, dopóki nie przeczytasz jego źródła i nie odkryjesz faktu, od którego zależy Logger
. Jeśli nie masz źródła, ogranicza to możliwość zrozumienia i efektywnego wykorzystania kodu, na którym polegasz.
Wzorzec singleton, zaimplementowany za pomocą statycznych właściwości / metod, to niewiele więcej niż sztuczka dotycząca implementowania infrastruktury. Ogranicza Cię na niezliczone sposoby, nie oferując dostrzegalnej korzyści w porównaniu z alternatywami. Możesz go używać w dowolny sposób, ale ponieważ istnieją realne alternatywy, które promują lepsze projektowanie, nigdy nie powinno to być zalecane.
Inni bardzo dobrze wyjaśnili ogólny problem z singletonami. Chciałbym tylko dodać uwagę na temat konkretnego przypadku Loggera. Zgadzam się z Tobą, że dostęp do Loggera (lub dokładniej mówiąc do roota) jako singletona zwykle nie stanowi problemu, za pomocą metody statycznej
getInstance()
lubgetRootLogger()
metody. (chyba, że chcesz zobaczyć, co jest rejestrowane przez klasę, którą testujesz - ale z mojego doświadczenia nie mogę sobie przypomnieć takich przypadków, w których było to konieczne. Z drugiej strony, dla innych może to być bardziej naglący problem).IMO zwykle pojedynczy rejestrator nie jest zmartwieniem, ponieważ nie zawiera żadnego stanu związanego z testowaną klasą. Oznacza to, że stan loggera (i jego możliwe zmiany) nie mają żadnego wpływu na stan testowanej klasy. Więc to nie utrudnia testów jednostkowych.
Alternatywą byłoby wstrzyknięcie rejestratora za pośrednictwem konstruktora do (prawie) każdej klasy w aplikacji. Dla spójności interfejsów należy go wstrzyknąć nawet, jeśli dana klasa obecnie niczego nie rejestruje - alternatywą byłoby to, że gdy odkryjesz w pewnym momencie, że teraz musisz coś zalogować z tej klasy, potrzebujesz loggera, a więc musisz dodać parametr konstruktora dla DI, łamiąc cały kod klienta. Nie podoba mi się obie te opcje i czuję, że używanie DI do logowania tylko skomplikowałoby moje życie, aby zachować zgodność z teoretyczną zasadą, bez żadnych konkretnych korzyści.
Więc moim zdaniem: klasa, która jest używana (prawie) powszechnie, ale nie zawiera stanu związanego z twoją aplikacją, może być bezpiecznie zaimplementowana jako Singleton .
źródło
Chodzi głównie o testy, ale nie do końca. Singltony były popularne ze względu na łatwość ich konsumowania, ale singletony mają wiele wad.
DI zapewnia łatwe wykorzystanie klas zależnych - po prostu umieść je w argumentach konstruktora, a system zapewni Ci to - jednocześnie zapewniając elastyczność testowania i konstrukcji.
źródło
Jedynym przypadkiem, w którym powinieneś kiedykolwiek używać Singletona zamiast Dependency Injection, jest sytuacja, gdy Singleton reprezentuje niezmienną wartość, taką jak List.Empty lub tym podobne (zakładając niezmienne listy).
Sprawdzenie wnętrzności dla Singletona powinno brzmieć „czy byłbym w porządku, gdyby była to zmienna globalna zamiast Singletona?” Jeśli nie, używasz wzorca Singleton do umieszczania na papierze zmiennej globalnej i powinieneś rozważyć inne podejście.
źródło
Właśnie przeczytałem artykuł Monostate - to fajna alternatywa dla Singletona, ale ma kilka dziwnych właściwości:
Czy to nie jest przerażające - ponieważ Mapper jest naprawdę zależny od połączenia z bazą danych, aby wykonać save () - ale jeśli wcześniej utworzono inny program mapujący - może pominąć ten krok w pozyskiwaniu jego zależności. Choć schludny, jest też trochę bałaganiarski, prawda?
źródło
Istnieją inne alternatywy dla wzorców Singleton: Proxy i MonoState.
http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf
Jak można użyć wzorca proxy do zastąpienia singletona?
źródło