Poniższa odpowiedź jest błędna, ale zatrzymam ją, aby inni mogli się z niej uczyć (patrz poniżej)
W ExampleA
można użyć tego samego Config
wystąpienia w wielu klasach. Jeśli jednak Config
w całej aplikacji powinna znajdować się tylko jedna instancja, rozważ zastosowanie wzorca Singleton, Config
aby uniknąć wielu instancji Config
. A jeśli Config
jest singletonem, możesz zamiast tego wykonać następujące czynności:
class ExampleA
{
private $config;
public function __construct()
{
$this->config = Config->getInstance();
}
}
$exampleA = new ExampleA();
Z ExampleB
drugiej strony, zawsze otrzymasz osobne wystąpienie Config
dla każdego wystąpienia ExampleB
.
Która wersja powinna zostać zastosowana, zależy od tego, jak aplikacja będzie obsługiwać wystąpienia Config
:
- jeśli każda instancja
ExampleX
powinna mieć osobną instancję Config
, idź z ExampleB
;
- jeśli każde wystąpienie
ExampleX
będzie dzielić jedno (i tylko jedno) wystąpienie Config
, użyj ExampleA with Config Singleton
;
- jeśli instancje
ExampleX
mogą używać różnych instancji Config
, trzymaj się ExampleA
.
Dlaczego przejście Config
na Singleton jest złe:
Muszę przyznać, że wczoraj dowiedziałem się o wzorze Singleton (czytając książkę wzorców projektowych Head First ). Naiwnie poszedłem i zastosowałem go w tym przykładzie, ale jak wielu zauważyło, jeden sposób jest inny (niektórzy byli bardziej tajemniczy i mówili tylko „robisz to źle!”), To nie jest dobry pomysł. Tak więc, aby inni nie popełnili tego samego błędu, który właśnie popełniłem, poniżej znajduje się podsumowanie, dlaczego wzorzec Singleton może być szkodliwy (na podstawie komentarzy i tego, co go znalazłem w Google):
Jeśli ExampleA
pobierze własne odwołanie do Config
instancji, klasy będą ściśle powiązane. Nie będzie ExampleA
możliwości użycia innej wersji Config
(powiedzmy podklasy). Jest to okropne, jeśli chcesz przetestować ExampleA
przy użyciu instancji makiety, Config
ponieważ nie ma sposobu, aby to zrobić ExampleA
.
Założeniem będzie jeden i tylko jeden przypadek, który Config
może się teraz utrzymywać , ale nie zawsze możesz być pewien, że to samo będzie obowiązywać w przyszłości . Jeśli w pewnym momencie okaże się, że Config
pożądanych będzie wiele instancji , nie ma możliwości osiągnięcia tego bez przepisania kodu.
Chociaż jedno i jedyne wystąpienie Config
może być prawdą przez całą wieczność, może się zdarzyć, że będziesz chciał móc skorzystać z jakiejś podklasy Config
(wciąż mając tylko jedno wystąpienie). Ale, ponieważ kod pobiera bezpośrednio poprzez wystąpienie getInstance()
o Config
, który jest static
metoda, nie ma sposobu, aby uzyskać podklasy. Ponownie kod musi zostać przepisany.
Fakt, że ExampleA
zastosowania Config
zostaną ukryte, przynajmniej podczas przeglądania interfejsu API ExampleA
. Może to być, ale nie musi, zła rzecz, ale osobiście uważam, że jest to niekorzystne; na przykład, utrzymując, nie ma prostego sposobu, aby dowiedzieć się, na które klasy będą miały wpływ zmiany, Config
bez patrzenia na implementację każdej innej klasy.
Nawet jeśli fakt, że ExampleA
używa się Singletona Config
, sam w sobie nie stanowi problemu, może on stać się problemem z testowego punktu widzenia. Obiekty Singleton będą nosić stan, który będzie trwał do momentu zakończenia aplikacji. Może to stanowić problem podczas uruchamiania testów jednostkowych, ponieważ chcesz, aby jeden test był izolowany od drugiego (tj. Że wykonanie jednego testu nie powinno wpływać na wynik innego). Aby to naprawić, obiekt Singleton musi zostać zniszczony między każdym uruchomieniem testowym (potencjalnie konieczne ponowne uruchomienie całej aplikacji), co może być czasochłonne (nie wspominając o żmudnych i irytujących).
Powiedziawszy to, cieszę się, że popełniłem ten błąd tutaj, a nie podczas wdrażania prawdziwej aplikacji. W rzeczywistości zastanawiałem się nad przepisaniem mojego najnowszego kodu w celu użycia wzorca Singleton dla niektórych klas. Chociaż mógłbym z łatwością cofnąć zmiany (oczywiście wszystko jest przechowywane w SVN), nadal zmarnowałbym czas na robienie tego.