Jeśli Singletony są złe, to dlaczego kontener SERWISOWY jest dobry?

Wszyscy wiemy jak Złe Singletony są, ponieważ ukrywają zależności i z innych powodów.

Ale w frameworku może być wiele obiektów, które muszą być utworzone tylko raz i wywołane zewsząd (logger, db itp.).

Aby rozwiązać ten problem, powiedziano mi, aby użyć tak zwanego "Menedżera obiektów" (lub kontenera usług, takiego jak symfony), który wewnętrznie przechowuje każde odniesienie do usług (logger itp.).

Ale dlaczego nie jest Dostawca usług tak zły jak czysty Singleton?

Dostawca usług również ukrywa zależności i po prostu kończy tworzenie pierwszej istance. Więc naprawdę trudno mi zrozumieć, dlaczego powinniśmy używać dostawcy usług zamiast singletonów.

PS. Wiem, że aby nie ukrywać zależności powinienem użyć DI (jak stwierdził Misko)

Dodaj

Dodam jeszcze: w dzisiejszych czasach singletony nie są aż tak złe, twórca PHPUnit wyjaśnił to tutaj:

DI + Singleton rozwiązuje problem:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }

        // ...
    }
}
?>
To całkiem mądre, nawet jeśli to nie rozwiąże wszystkich problemów.

INNE niż di I Service Container czy istnieje jakieś dobre, dopuszczalne rozwiązanie , aby uzyskać dostęp do tych obiektów pomocniczych?

Author: Marco Demaio, 2011-05-17

5 answers

Service Locator to tylko mniejsze zło, że tak powiem. "Mniejsi" sprowadzają się do tych czterech różnic (przynajmniej nie mogę teraz myśleć o innych):

Zasada Jednej Odpowiedzialności

Service Container nie narusza zasady pojedynczej odpowiedzialności, tak jak Singleton. Singletons łączy tworzenie obiektów z logiką biznesową, podczas gdy kontener usług jest ściśle odpowiedzialny za zarządzanie cyklem życia obiektów w aplikacji. W tym zakresie Kontener SERWISOWY jest lepszy.

Sprzęgło

Singletony są zwykle zakodowane na twardo w aplikacji ze względu na statyczne wywołania metod, co prowadzi do ściśle powiązanych i trudnych do naśladowania zależności w kodzie. SL z drugiej strony jest tylko jedną klasą i można go wstrzykiwać. Tak więc, podczas gdy wszystkie Twoje klasy będą od niej zależeć, przynajmniej jest to luźno powiązana zależność. Jeśli więc nie zaimplementowałeś ServiceLocator jako Singleton, jest to nieco lepsze i łatwiejsze do test.

Jednak wszystkie klasy używające ServiceLocator będą teraz zależeć od ServiceLocator, który jest również formą sprzężenia. Można to złagodzić za pomocą interfejsu dla ServiceLocator, więc nie jesteś związany z konkretną implementacją ServiceLocator, ale twoje klasy będą zależeć od istnienia jakiegoś lokalizatora, podczas gdy nie używanie ServiceLocator w ogóle zwiększa ponowne użycie dramatycznie.

Ukryte Zależności

Problem ukrywania zależności bardzo istnieje jednak dalej. Kiedy po prostu wstrzykniesz lokalizator do klas zużywających się, nie będziesz znał żadnych zależności. Ale w przeciwieństwie do Singletona, SL zwykle tworzy instancje wszystkich zależności potrzebnych za kulisami. Więc kiedy pobierasz Usługę, nie kończysz jak Misko Hevery w przykładzie karty kredytowej, np. nie musisz ręcznie tworzyć instancji wszystkich zależności zależności.

Pobieranie zależności z wnętrza instancji również narusza prawo Demeter , które mówi, że nie należy kopać w kolaborantów. Instancja powinna rozmawiać tylko ze swoimi bezpośrednimi współpracownikami. Jest to problem zarówno Singleton jak i ServiceLocator.

Stan Globalny

Problem stanu globalnego jest również nieco złagodzony, ponieważ gdy tworzysz instancję nowego lokalizatora usług między testami, wszystkie wcześniej utworzone instancje są również usuwane(chyba że popełniłeś błąd i zapisałeś je w atrybutach statycznych w SL). Że oczywiście nie dotyczy żadnego stanu globalnego w klasach zarządzanych przez SL.

Zobacz również Fowler na Service Locator vs Dependency Injection aby uzyskać bardziej dogłębną dyskusję.


Uwaga na temat Twojej aktualizacji i powiązany artykuł Sebastiana Bergmanna na temat testowania kodu używającego singletonów : Sebastian w żaden sposób nie sugeruje, że proponowane obejście sprawia, że używanie Singleonów jest mniej problematyczne. Jest to tylko jeden ze sposobów tworzenia kodu, który w przeciwnym razie byłby niemożliwe do przetestowania bardziej testowalne. Ale to wciąż problematyczny kod. W rzeczywistości wyraźnie zauważa: "to, że możesz, nie znaczy, że powinieneś".

 75
Author: Gordon,
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-03-27 12:32:12

Wzorzec lokalizatora usług jest wzorzec anty. Nie rozwiązuje to problemu ujawnienia zależności (nie można stwierdzić Po spojrzeniu na definicję klasy, jakie są jej zależności, ponieważ nie są one wstrzykiwane, zamiast tego są wyciągane z Lokalizatora usług).

Twoje pytanie brzmi: dlaczego lokalizatory usług są dobre? Moja odpowiedź brzmi: nie są.

Unikaj, unikaj, unikaj.

 42
Author: jason,
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
2011-05-25 17:50:32

Kontener usługi ukrywa zależności tak, jak robi to wzór Singletona. Możesz zamiast tego zasugerować użycie kontenerów iniekcji zależności, ponieważ ma on wszystkie zalety service container, ale nie ma (o ile mi wiadomo) wad, które ma service container.

O ile to Rozumiem, jedyną różnicą między tymi dwoma jest to, że w kontenerze usług, kontener usług jest wstrzykiwany obiekt (ukrywając zależności), gdy używasz DIC, DIC wstrzykuje odpowiednie zależności dla Ciebie. Klasa zarządzana przez DIC jest całkowicie nieświadoma faktu, że jest zarządzana przez DIC, więc masz mniej sprzężeń, jasne zależności i szczęśliwe testy jednostkowe.

To jest dobre pytanie w tak wyjaśnieniu różnicy obu: Jaka jest różnica między wzorami iniekcji zależności A lokalizatorem usług?

 3
Author: rickchristie,
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 12:09:42

Ponieważ można łatwo zastąpić obiekty w kontenerze usług przez
1) dziedziczenie (Klasa Menedżera obiektów może być dziedziczona, a metody mogą być nadpisywane)
2) zmiana konfiguracji (w przypadku Symfony)

I, Singletony są złe nie tylko z powodu wysokiego sprzężenia, ale dlatego, że są _ Single _tons. To zła Architektura dla prawie wszystkich rodzajów obiektów.

Z' pure ' DI (W konstruktorach) zapłacisz bardzo dużą cenę - wszystkie obiekty powinny być tworzone przed przekazaniem w konstruktorze. Oznacza to więcej zużywanej pamięci i mniejszą wydajność. Ponadto nie zawsze obiekt może być po prostu wytworzony i przekazany w konstruktorze - łańcuch zależności może być wytworzony... Mój angielski nie jest wystarczająco dobry, aby o tym całkowicie dyskutować, poczytaj o tym w dokumentacji Symfony.

 1
Author: OZ_,
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
2011-05-17 18:07:45

Dla mnie staram się unikać stałych globalnych, singletonów z prostego powodu, są przypadki, w których może być konieczne uruchomienie API.

Na przykład, mam front-end i admin. Wewnątrz admin, chcę, aby mogli się zalogować jako użytkownik. Rozważ kod w adminie.

$frontend = new Frontend();
$frontend->auth->login($_GET['user']);
$frontend->redirect('/');

To może ustanowić nowe połączenie z bazą danych, Nowy logger itp. dla inicjalizacji frontendu i sprawdzić, czy użytkownik rzeczywiście istnieje, poprawny itd. Wykorzystywałaby również odpowiednie oddzielne pliki cookie i lokalizację usługi.

Mój pomysł Singletona jest taki, że nie można dodać tego samego obiektu w rodzicu dwa razy. Na przykład

$logger1=$api->add('Logger');
$logger2=$api->add('Logger');

Zostawi pojedynczą instancję i obie zmienne wskazujące na nią.

Na koniec, jeśli chcesz używać programowania obiektowego, to pracuj z obiektami, a nie z klasami.

 0
Author: romaninsh,
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
2011-05-20 09:58:32