Gloryfikowana zmienna globalna - staje się chwalebną klasą globalną. Niektórzy twierdzą, że łamanie zorientowanego obiektowo projektu.
Podaj mi scenariusze inne niż stary dobry rejestrator, w którym sensowne jest użycie singletonu.
Gloryfikowana zmienna globalna - staje się chwalebną klasą globalną. Niektórzy twierdzą, że łamanie zorientowanego obiektowo projektu.
Podaj mi scenariusze inne niż stary dobry rejestrator, w którym sensowne jest użycie singletonu.
Odpowiedzi:
W mojej pogoni za prawdą odkryłem, że tak naprawdę jest bardzo niewiele „akceptowalnych” powodów, aby używać Singletona.
Jednym z powodów, który często pojawia się w sieci, jest klasa „rejestrowania” (o której wspominałeś). W takim przypadku Singleton może być użyty zamiast pojedynczej instancji klasy, ponieważ klasa rejestrowania zwykle musi być używana w kółko przez każdą klasę w projekcie. Jeśli każda klasa korzysta z tej klasy rejestrowania, wstrzykiwanie zależności staje się niewygodne.
Rejestrowanie jest szczególnym przykładem „akceptowalnego” Singletona, ponieważ nie wpływa on na wykonanie twojego kodu. Wyłącz rejestrowanie, wykonywanie kodu pozostaje takie samo. Włącz to samo. Misko umieszcza to w następujący sposób w Root Cause of Singletons : „Informacje przepływają tutaj w jedną stronę: z aplikacji do rejestratora. Mimo że rejestratory są stanem globalnym, ponieważ żadne informacje nie przepływają z rejestratorów do aplikacji, rejestratory są dopuszczalne”.
Jestem pewien, że istnieją również inne ważne powody. Alex Miller w „ Wzorcach, których nienawidzę ”, mówi o lokalizatorach usług i interfejsie użytkownika po stronie klienta, które są prawdopodobnie „akceptowalnymi” wyborami.
Kandydat Singleton musi spełniać trzy wymagania:
Jeśli proponowany Singleton ma tylko jeden lub dwa z tych wymagań, przeprojektowanie jest prawie zawsze właściwą opcją.
Na przykład jest mało prawdopodobne, aby bufor drukarki był wywoływany z więcej niż jednego miejsca (menu Drukuj), więc można użyć muteksów do rozwiązania problemu z równoczesnym dostępem.
Prosty program rejestrujący jest najbardziej oczywistym przykładem potencjalnie poprawnego Singletona, ale może się to zmienić w przypadku bardziej złożonych schematów rejestrowania.
Odczytywanie plików konfiguracyjnych, które powinny być odczytywane tylko podczas uruchamiania i zamykanie ich w singletonie.
Properties.Settings.Default
w .NET.
Z singletonu korzystasz, gdy chcesz zarządzać udostępnionym zasobem. Na przykład bufor drukarki. Twoja aplikacja powinna mieć tylko jedną instancję bufora, aby uniknąć sprzecznego żądania dla tego samego zasobu.
Lub połączenie z bazą danych lub menedżer plików itp.
Tylko do odczytu singletony przechowujące stan globalny (język użytkownika, ścieżka pliku pomocy, ścieżka aplikacji) są rozsądne. Uważaj na używanie singletonów do kontrolowania logiki biznesowej - pojedynczy prawie zawsze kończy się wieloma
Zarządzanie połączeniem (lub pulą połączeń) z bazą danych.
Użyłbym tego również do pobierania i przechowywania informacji w zewnętrznych plikach konfiguracyjnych.
Jednym ze sposobów korzystania z singletonu jest pokrycie instancji, w której musi istnieć pojedynczy „broker” kontrolujący dostęp do zasobu. Singletony są dobre w programach rejestrujących, ponieważ pośredniczą w dostępie do, powiedzmy, pliku, do którego można zapisać wyłącznie. W przypadku czegoś takiego jak rejestrowanie, zapewniają one sposób wyodrębnienia zapisów do czegoś takiego jak plik dziennika - możesz zawinąć mechanizm buforowania w singletonie itp.
Pomyśl także o sytuacji, w której masz aplikację z wieloma oknami / wątkami / itp., Ale która wymaga jednego punktu komunikacji. Kiedyś użyłem jednego do kontrolowania zadań, które chciałem uruchomić moją aplikację. Singleton był odpowiedzialny za serializację zadań i wyświetlanie ich statusu w dowolnej innej części programu, która była zainteresowana. W tego rodzaju scenariuszu możesz spojrzeć na singleton jako coś w rodzaju klasy „serwerowej” działającej w Twojej aplikacji ... HTH
Do zarządzania dostępem do zasobu współużytkowanego przez całą aplikację powinien być używany singleton, a potencjalnie posiadanie wielu wystąpień tej samej klasy byłoby destrukcyjne. Zapewnienie, że dostęp do wątku współdzielonego jest bezpieczny, jest bardzo dobrym przykładem tego, gdzie tego rodzaju wzorzec może być niezbędny.
Korzystając z Singletonów, upewnij się, że przypadkowo nie ukrywasz zależności. Idealnie, singletony (jak większość zmiennych statycznych w aplikacji) są konfigurowane podczas wykonywania kodu inicjalizacji aplikacji (static void Main () dla plików wykonywalnych C #, static void main () dla plików wykonywalnych Java), a następnie przekazywane do wszystkie inne klasy, które są tworzone, które tego wymagają. Pomaga to utrzymać testowalność.
Praktyczny przykład singletona można znaleźć w Test :: Builder , klasie, która wspiera prawie każdy nowoczesny moduł testujący Perla. Singleton Test :: Builder przechowuje i brokuje stan i historię procesu testowego (historyczne wyniki testu, zlicza liczbę uruchomionych testów), a także rzeczy takie jak miejsce, w którym idzie wynik testu. Wszystkie są niezbędne do koordynowania wielu modułów testowych napisanych przez różnych autorów, aby współpracować w jednym skrypcie testowym.
Historia singla Test :: Builder ma charakter edukacyjny. Wywołanie new()
zawsze daje ten sam obiekt. Po pierwsze, wszystkie dane były przechowywane jako zmienne klas bez niczego w samym obiekcie. Działało to do momentu, gdy sam chciałem przetestować Test :: Builder. Następnie potrzebowałem dwóch obiektów Test :: Builder, jednego zestawu jako manekina, aby uchwycić i przetestować jego zachowanie i dane wyjściowe, a drugiego być prawdziwym obiektem testowym. W tym momencie Test :: Builder został przekształcony w prawdziwy obiekt. Obiekt singletonu był przechowywany jako dane klasy i new()
zawsze go zwracał. create()
został dodany, aby utworzyć świeży obiekt i umożliwić testowanie.
Obecnie użytkownicy chcą zmienić niektóre zachowania Test :: Builder we własnym module, ale zostawić inne w spokoju, podczas gdy historia testów pozostaje wspólna dla wszystkich modułów testujących. To, co się teraz dzieje, to monolityczny obiekt Test :: Builder jest rozkładany na mniejsze części (historia, dane wyjściowe, format ...), a instancja Test :: Builder zbiera je razem. Teraz Test :: Builder nie musi już być singletonem. Składniki, takie jak historia, mogą być. Spycha to nieelastyczną potrzebę singletona na niższy poziom. Daje użytkownikowi większą elastyczność w mieszaniu i dopasowywaniu elementów. Mniejsze obiekty singletonowe mogą teraz po prostu przechowywać dane, a zawierające je obiekty decydują o tym, jak z nich korzystać. Pozwala nawet na grę poza klasą :: Test :: Builder za pomocą historii Test :: Builder i singletonów wyjściowych.
Wydaje się, że istnieje koordynacja danych z elastycznością zachowań, którą można złagodzić, umieszczając singleton wokół tylko współdzielonych danych z możliwie najmniejszym zachowaniem, aby zapewnić integralność danych.
Gdy ładujesz obiekt właściwości konfiguracji, albo z bazy danych, albo z pliku, pomaga zachować go jako singleton; nie ma powodu, aby nadal odczytywać dane statyczne, które nie zmienią się podczas działania serwera.
Możesz użyć Singleton podczas implementacji wzorca stanu (w sposób pokazany w książce GoF). Jest tak, ponieważ konkretne klasy stanu nie mają własnego stanu i wykonują swoje działania w kategoriach klasy kontekstu.
Możesz także sprawić, że Abstract Factory stanie się singletonem.
setState()
odpowiedzialny za decydowanie o polityce tworzenia stanu. Pomaga, jeśli Twój język programowania obsługuje szablony lub ogólne. Zamiast Singleton można użyć wzorca Monostate , w którym tworzenie instancji obiektu stanu powoduje ponowne użycie tego samego globalnego / statycznego obiektu stanu. Składnia zmiany stanu może pozostać niezmieniona, ponieważ użytkownicy nie muszą być świadomi, że utworzony stan jest monostatem.
Wspólne zasoby. Zwłaszcza w PHP, klasie bazy danych, klasie szablonów i klasie globalnych magazynów zmiennych. Wszystkie muszą być współużytkowane przez wszystkie moduły / klasy używane w całym kodzie.
Jest to prawdziwe użycie obiektu -> klasa szablonów zawiera budowany szablon strony, który jest kształtowany, dodawany, zmieniany przez moduły dodające do danych wyjściowych strony. Musi być przechowywany jako pojedyncza instancja, aby tak się stało, to samo dotyczy baz danych. Dzięki współużytkowanemu singletonowi bazy danych wszystkie klasy modułów mogą uzyskać dostęp do zapytań i uzyskać je bez konieczności ich ponownego uruchamiania.
Singleton globalnego składu zmiennych zapewnia globalny, niezawodny i łatwy w użyciu skład zmiennych. Bardzo porządkuje Twój kod. Wyobraź sobie, że wszystkie wartości konfiguracji znajdują się w tablicy w singletonie, np .:
$gb->config['hostname']
lub posiadanie wszystkich wartości językowych w tablicy takiej jak:
$gb->lang['ENTER_USER']
Pod koniec uruchamiania kodu strony otrzymujesz, powiedzmy, teraz dojrzały:
$template
Singleton, $gb
singleton, który ma tablicę językową do zastąpienia go i wszystkie dane wyjściowe są załadowane i gotowe. Po prostu zamienisz je na klucze obecne w wartości strony obiektu dojrzałego szablonu, a następnie podasz je użytkownikowi.
Ogromną zaletą tego jest to, że możesz wykonać DOWOLNĄ obróbkę końcową na dowolnym urządzeniu. Możesz przesyłać wszystkie wartości językowe do tłumaczeń google lub innej usługi tłumaczeniowej i odzyskać je, a następnie zamienić na odpowiednie miejsca, na przykład przetłumaczone. lub możesz zastępować w strukturach strony lub ciągach treści, jak chcesz.
Konfigurowanie określonych problemów związanych z infrastrukturą jako singletonów lub zmiennych globalnych może być bardzo pragmatyczne. Moim ulubionym przykładem są frameworki Dependency Injection, które wykorzystują singletony do działania jako punkt połączenia z frameworkiem.
W takim przypadku polegasz na infrastrukturze, aby uprościć korzystanie z biblioteki i uniknąć niepotrzebnej złożoności.
Używam go do obiektów enkapsulujących parametry wiersza poleceń podczas pracy z modułami wtykowymi. Główny program nie wie, jakie są parametry wiersza poleceń dla modułów, które się ładują (i nie zawsze nawet wie, które moduły są ładowane). np. główne obciążenia A, które same nie potrzebują żadnych parametrów (więc dlaczego powinien wziąć dodatkowy wskaźnik / odniesienie / cokolwiek, nie jestem pewien - wygląda jak zanieczyszczenie), a następnie ładuje moduły X, Y i Z. Dwa z nich, powiedzmy X i Z, potrzebujesz (lub akceptuje) parametry, więc wywołują z powrotem do singletonu wiersza poleceń, aby powiedzieć mu, jakie parametry zaakceptować, a w czasie wykonywania wywołują z powrotem, aby dowiedzieć się, czy użytkownik faktycznie podał jakieś z nich.
Pod wieloma względami singleton do obsługi parametrów CGI działałby podobnie, jeśli używasz tylko jednego procesu na zapytanie (inne metody mod_ * tego nie robią, więc byłoby źle - stąd argument, że nie powinieneś „ t używaj singletonów w świecie mod_cgi na wypadek, gdybyś portował do mod_perl lub dowolnego innego świata).
Być może przykład z kodem.
Tutaj ConcreteRegistry jest singlem w pokerze, który pozwala zachowaniom w górę drzewa pakietów uzyskać dostęp do kilku podstawowych interfejsów gry (tj. Fasad modelu, widoku, kontrolera, środowiska itp.):
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
Ed.
1 - Komentarz do pierwszej odpowiedzi:
Nie zgadzam się ze statyczną klasą Logger. może to być praktyczne w przypadku implementacji, ale nie może być zastąpione w testach jednostkowych. Klasy statycznej nie można zastąpić podwójnym testem. Jeśli nie przeprowadzisz testu jednostkowego, nie zobaczysz tutaj problemu.
2 - Staram się nie tworzyć singletonu ręcznie. Po prostu tworzę prosty obiekt z konstruktorami, który pozwala mi wstrzykiwać współpracowników do obiektu. Gdybym potrzebował singletona, użyłbym frameworku wyboru zależności (Spring.NET, Unity dla .NET, Spring dla Java) lub innego.
ILogger logger = Logger.SingleInstance();
ta metoda jest statyczna i zwraca statycznie zachowaną instancję ILoggera. Użyłeś przykładu „środowiska wstrzykiwania zależności”. Prawie wszystkie pojemniki DI są singletonami; ich konfiguracje są zdefiniowane statycznie i ostatecznie dostępne z / przechowywane w interfejsie jednego usługodawcy.