Mamy klasę, która zawiera informacje o konfiguracji aplikacji. Kiedyś to był singleton. Po pewnym przeglądzie architektonicznym kazano nam usunąć singleton. Zauważyliśmy pewne korzyści wynikające z niestosowania singletona w testach jednostkowych, ponieważ możemy testować różne konfiguracje jednocześnie.
Bez singletona musimy przekazać instancję wszędzie w naszym kodzie. Robi się taki bałagan, więc napisaliśmy opakowanie singletona. Teraz przenosimy ten sam kod do PHP i .NET, zastanawiam się, czy istnieje lepszy wzorzec, którego możemy użyć dla obiektu konfiguracyjnego.
źródło
Najlepszym sposobem jest użycie zamiast tego wzorca Factory. Podczas konstruowania nowej instancji swojej klasy (w fabryce) możesz wstawić dane „globalne” do nowo utworzonego obiektu, jako odniesienie do pojedynczej instancji (którą przechowujesz w klasie fabrycznej) lub kopiując odpowiednie dane do nowego obiektu.
Wszystkie twoje obiekty będą wtedy zawierały dane, które kiedyś żyły w singletonie. Nie sądzę, aby ogólnie różnica była duża, ale może to ułatwić czytanie kodu.
źródło
Mogę tu stwierdzić oczywiste, ale czy jest powód, dla którego nie można używać frameworka wstrzykiwania zależności, takiego jak Spring lub Guice ? (Wierzę, że Spring jest teraz również dostępny dla .NET).
W ten sposób framework może pomieścić pojedynczą kopię obiektów konfiguracyjnych, a Twoje komponenty bean (usługi, DAO, cokolwiek) nie muszą się martwić o ich wyszukiwanie.
To jest podejście, które zwykle wybieram!
źródło
Jeśli używasz Spring Framework , możesz po prostu stworzyć zwykły bean. Domyślnie (lub jeśli jawnie ustawisz
scope="singleton"
) tylko jedna instancja komponentu bean jest tworzona i ta instancja jest zwracana za każdym razem, gdy komponent bean jest używany w zależności lub pobierany za pośrednictwemgetBean()
.Zyskujesz przewagę pojedynczego wystąpienia, bez sprzężenia wzorca Singleton.
źródło
Alternatywą jest przekazanie tego, czego potrzebujesz, zamiast proszenia obiektu o rzeczy.
źródło
nie kumuluj odpowiedzialności za pojedynczy obiekt konfiguracyjny, ponieważ kończy się to bardzo dużym obiektem, który jest zarówno trudny do zrozumienia, jak i delikatny.
Na przykład, jeśli potrzebujesz innego parametru do określonej klasy, zmieniasz
Configuration
obiekt, a następnie ponownie skompiluj wszystkie klasy, które go używają. To jest trochę problematyczne.Spróbuj refaktoryzować swój kod, aby uniknąć wspólnego, globalnego i dużego
Configuration
obiektu. Przekaż tylko wymagane parametry do klas klienta:należy refaktoryzować na:
ramy wtrysk zależność pomogą wiele, ale to nie jest stricly wymagane.
źródło
Możesz osiągnąć to samo zachowanie singletona, używając metod statycznych. Steve Yegge wyjaśnia to bardzo dobrze w tym poście.
źródło
Czy klasa, która zawiera tylko statyczne metody i pola, jest możliwa? Nie jestem pewien, jaka jest Twoja sytuacja, ale warto się temu przyjrzeć.
źródło
Zależy od tego, jakie narzędzia / ramy itp. Są używane. W przypadku narzędzi do iniekcji zależności / ioc często można nadal uzyskać pojedynczą wydajność / optymalizację, jeśli kontener di / ioc używa zachowania singleton dla wymaganej klasy - (na przykład interfejsu IConfigSettings), tworząc tylko jedną instancję klasy. Można to nadal zastąpić do testowania
Alternatywnie można użyć fabryki, aby utworzyć klasę i zwrócić tę samą instancję za każdym razem, gdy jej zażądasz - ale w celu przetestowania może zwrócić wersję skróconą / fałszywą
źródło
Przejrzyj możliwość konfiguracji jako interfejsu wywołań zwrotnych. Więc twój kod wrażliwy na konfigurację będzie wyglądał:
Kod startowy systemu będzie wyglądał:
źródło
Można użyć struktury iniekcji zależności, aby złagodzić ból związany z przekazywaniem obiektu konfiguracyjnego. Przyzwoity jest ninject, który ma tę zaletę, że używa kodu zamiast XML.
źródło
Może też niezbyt czysty, ale może możesz przekazać bity informacyjne, które chcesz zmienić, do metody, która tworzy singleton - zamiast używać
możesz wywołać
createSingleton(Information info)
bezpośrednio przy starcie aplikacji (oraz w metodzie setUp testów jednostkowych).źródło
Singletony nie są złe, ale wzór projektu jest wadliwy. Mam klasę, w której chcę utworzyć tylko jedno jej wystąpienie w czasie wykonywania, ale chcę utworzyć wiele izolowanych wystąpień podczas testów jednostkowych, aby zapewnić deterministyczne wyniki.
DI przy użyciu Springa itp. Jest bardzo dobrą opcją, ale nie jedyną.
źródło