Czy istnieje przypadek użycia dla singletonów z dostępem do bazy danych w PHP?

138

Uzyskuję dostęp do mojej bazy danych MySQL przez PDO. Konfiguruję dostęp do bazy danych i moja pierwsza próba polegała na użyciu:

Pierwsza rzecz, o której pomyślałem, to global:

$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

Jest to uważane za złą praktykę. Po krótkich poszukiwaniach znalazłem wzór Singleton , który

„dotyczy sytuacji, w których musi istnieć jedna instancja klasy”.

Zgodnie z przykładem w instrukcji powinniśmy to zrobić:

class Database {
    private static $instance, $db;

    private function __construct(){}

    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;

        return self:$instance;
    }

    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd')

        return self::$db;
    }
}

function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}

some_function();

Po co mi ta stosunkowo duża klasa, skoro mogę to zrobić?

class Database {
    private static $db;

    private function __construct(){}

    static function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd');

        return self::$db;
    }
}

function some_function() {
    Database::get()->query('...');
}

some_function();

Ten ostatni działa idealnie i nie muszę się $dbjuż o niego martwić .

Jak mogę utworzyć mniejszą klasę pojedynczą lub czy istnieje przypadek użycia dla singletonów, którego brakuje w PHP?

seriousdev
źródło
Jest wiele zasobów i dyskusji w tym pokrewnym pytaniu: „Co jest takiego złego w singletonach?”
FruitBreak

Odpowiedzi:

80

Okej, zastanawiałem się nad tym przez chwilę, kiedy zaczynałem karierę. Zaimplementowałem to na różne sposoby i wymyśliłem dwa powody, aby nie używać klas statycznych, ale są one dość duże.

Jednym z nich jest to, że bardzo często znajdziesz coś, czego jesteś absolutnie pewien, że nigdy nie będziesz mieć więcej niż jednej instancji, w końcu masz drugą. Możesz skończyć z drugim monitorem, drugą bazą danych, drugim serwerem - cokolwiek.

Kiedy tak się dzieje, jeśli używałeś klasy statycznej, czeka cię znacznie gorszy refaktor niż gdybyś używał singletona. Singleton jest sam w sobie niepewnym wzorcem, ale dość łatwo przekształca się w inteligentny wzorzec fabryki - można go nawet przekonwertować na użycie iniekcji zależności bez większych problemów. Na przykład, jeśli twój singleton jest przesyłany przez getInstance (), możesz łatwo to zmienić na getInstance (databaseName) i zezwolić na wiele baz danych - bez innych zmian w kodzie.

Druga kwestia to testowanie (i szczerze mówiąc, to to samo, co pierwsza kwestia). Czasami chcesz zastąpić bazę danych symulowaną bazą danych. W efekcie jest to druga instancja obiektu bazy danych. Jest to o wiele trudniejsze do zrobienia w przypadku klas statycznych niż w przypadku singletonów, wystarczy tylko wyszydzić metodę getInstance (), a nie każdą metodę w klasie statycznej (co w niektórych językach może być bardzo trudne).

To naprawdę sprowadza się do nawyków - a kiedy ludzie mówią, że „Globale” są złe, mają bardzo dobre powody, aby to powiedzieć, ale nie zawsze jest to oczywiste, dopóki sam nie rozwiążesz problemu.

Najlepsze, co możesz zrobić, to zapytać (tak jak to zrobiłeś), a następnie dokonać wyboru i obserwować konsekwencje swojej decyzji. Posiadanie wiedzy pozwalającej interpretować ewolucję kodu w czasie jest o wiele ważniejsze niż robienie tego dobrze.

Bill K.
źródło
15
Mówisz, że singletony ładnie degradują się do DI, ale czy twój przykład nie jest getInstance(databaseName)wciąż tylko rozpraszaniem odniesień do globalnego repozytorium instancji w całym kodzie? Kod, który miałby wywołać, getInstancepowinien mieć instancję (y) wstrzykniętą (e) do niego przez kod klienta, a więc nie powinien getInstancew pierwszej kolejności wywoływać .
Will Vousden,
1
@ Will Vousden Prawidłowo, to rodzaj przerwy. To nie jest tak naprawdę DI, ale może być całkiem blisko. Na przykład, co by było, gdyby była to getInstance (supportedDatabase), a zwrócona instancja została obliczona na podstawie przekazanej bazy danych? Chodzi o to, aby nie przerażać ludzi frameworkiem DI, dopóki nie będą na to gotowi.
Bill K,
320

Singletony mają bardzo mało - jeśli nie powiedzieć nie - zastosowania w PHP.

W językach, w których obiekty żyją w pamięci współdzielonej, Singletons mogą być używane do utrzymywania niskiego zużycia pamięci. Zamiast tworzyć dwa obiekty, odwołujesz się do istniejącej instancji z globalnie współużytkowanej pamięci aplikacji. W PHP nie ma takiej pamięci aplikacji. Singleton utworzony w jednym żądaniu żyje dokładnie dla tego żądania. Singleton utworzony w innym żądaniu wykonanym w tym samym czasie to wciąż zupełnie inna instancja. Tak więc jeden z dwóch głównych celów Singletona nie ma tutaj zastosowania.

Ponadto wiele obiektów, które koncepcyjnie mogą istnieć tylko raz w aplikacji, niekoniecznie wymaga mechanizmu językowego, aby to wymusić. Jeśli potrzebujesz tylko jednej instancji, nie twórz innej . Tylko wtedy, gdy możesz nie mieć innej instancji, np. Gdy kocięta giną podczas tworzenia drugiej instancji, możesz mieć ważny przypadek użycia dla singletona.

Innym celem byłoby posiadanie globalnego punktu dostępu do instancji w ramach tego samego żądania. Chociaż może się to wydawać pożądane, w rzeczywistości tak nie jest, ponieważ tworzy sprzężenie z zakresem globalnym (jak wszystkie globale i statyki). To sprawia, że ​​testy jednostkowe są trudniejsze, a aplikacja jest ogólnie trudniejsza w utrzymaniu. Istnieją sposoby na złagodzenie tego problemu, ale ogólnie, jeśli chcesz mieć tę samą instancję w wielu klasach, użyj funkcji Dependency Injection .

Zobacz moje slajdy dla singletonów w PHP - dlaczego są złe i jak możesz je wyeliminować z aplikacji, aby uzyskać dodatkowe informacje.

Nawet Erich Gamma , jeden z wynalazców wzoru Singleton, wątpi obecnie w ten wzór:

„Jestem za rezygnacją z Singletona. Jego użycie jest prawie zawsze zapachem projektowym”

Dalsza lektura

Jeśli po powyższym nadal potrzebujesz pomocy w podjęciu decyzji:

Diagram decyzyjny singletona

Gordon
źródło
1
@Gordon tak. I nawet jeśli możliwe było utrzymywanie obiektów między żądaniami, Singletony nadal naruszają kilka zasad SOLID i wprowadzają Global State.
Gordon,
4
Przykro mi, że idę pod prąd, ale DI nie jest tak naprawdę rozwiązaniem problemu, do którego jest używany Singleton, chyba że jesteś zadowolony z posiadania klas z 42 parametrami ctor (lub 42 wywołaniami setFoo () i setBar (), aby to zrobić praca). Tak, niestety niektóre aplikacje muszą być tak połączone i zależą od wielu zewnętrznych czynników. PHP jest językiem klejenia i czasami jest wiele rzeczy do sklejenia.
StasM
14
@StasM, jeśli masz 42 parametry ctor lub potrzebujesz wielu seterów, robisz to źle. Obejrzyj proszę rozmowy o czystym kodzie. Przepraszam, jeśli nie chce mi się tego wyjaśniać innym razem. Więcej informacji można uzyskać na czacie PHP.
Gordon
@Gordon Gdzie jest pokój rozmów php?
user658182
21

Kto potrzebuje singletonów w PHP?

Zauważ, że prawie wszystkie zastrzeżenia do singletonów pochodzą z technicznego punktu widzenia - ale są one również BARDZO ograniczone w swoim zakresie. Specjalnie dla PHP. Najpierw wymienię niektóre powody używania singletonów, a następnie przeanalizuję zastrzeżenia do używania singletonów. Po pierwsze, ludzie, którzy ich potrzebują:

- Osoby, które kodują duże frameworki / bazy kodu, które będą wykorzystywane w wielu różnych środowiskach, będą musiały pracować z wcześniej istniejącymi, różnymi frameworkami / bazami kodu, z koniecznością implementacji wielu różnych, zmieniających się, a nawet kapryśnych żądań klientów / szefów / kierownictwo / liderzy jednostek tak.

Widzicie, wzorzec singletona jest samowyłączny. Po zakończeniu klasa pojedyncza jest sztywna w każdym kodzie, w którym ją umieścisz, i działa dokładnie tak, jak utworzyłeś jej metody i zmienne. I jest to zawsze ten sam obiekt w danym zapytaniu. Ponieważ nie można utworzyć go dwukrotnie, aby był dwoma różnymi obiektami, wiesz, czym jest obiekt singleton w dowolnym punkcie kodu - nawet jeśli singleton jest wstawiony do dwóch, trzech różnych, starych, a nawet spaghetti baz kodów. W związku z tym jest to łatwiejsze w celach programistycznych - nawet jeśli w tym projekcie pracuje wiele osób, kiedy widzisz, że singleton jest inicjalizowany w jednym punkcie w danym kodzie, wiesz, co to jest, co robi, jak działa i stan, w jakim się znajduje. Gdyby to była tradycyjna klasa, należałoby śledzić, gdzie został utworzony ten obiekt, jakie metody zostały w nim wywołane do tego momentu w kodzie i jaki jest jego stan. Ale upuść tam singletona, a jeśli porzuciłeś właściwe metody debugowania i informacji oraz śledzenie na singletonie podczas kodowania, wiesz dokładnie, co to jest. Ułatwia to więc pracę osobom, które mają do czynienia z różnymi bazami kodu, z koniecznością integracji kodu, co zostało zrobione wcześniej z różnymi filozofiami lub przez osoby, z którymi nie masz kontaktu. (to znaczy dostawca-firma-projektowa-czegokolwiek już nie ma, nie ma żadnego wsparcia). ułatwia to pracę osobom, które mają do czynienia z różnymi bazami kodu, z koniecznością integracji kodu, co zostało zrobione wcześniej z różnymi filozofiami lub przez osoby, z którymi nie masz kontaktu. (to znaczy dostawca-firma-projektowa-czegokolwiek już nie ma, nie ma żadnego wsparcia). ułatwia to pracę osobom, które mają do czynienia z różnymi bazami kodu, z koniecznością integracji kodu, co zostało zrobione wcześniej z różnymi filozofiami lub przez osoby, z którymi nie masz kontaktu. (to znaczy dostawca-firma-projektowa-czegokolwiek już nie ma, nie ma żadnego wsparcia).

- Osoby, które muszą pracować z interfejsami API , usługami i witrynami internetowymi innych firm.

Jeśli przyjrzysz się bliżej, nie różni się to zbytnio od wcześniejszego przypadku - zewnętrzne interfejsy API, usługi, strony internetowe są jak zewnętrzne, izolowane bazy kodów, nad którymi NIE masz kontroli. Wszystko może się zdarzyć. Tak więc, dzięki pojedynczej sesji / klasie użytkownika, możesz zarządzać DOWOLNYM rodzajem implementacji sesji / autoryzacji od zewnętrznych dostawców, takich jak OpenID , Facebook , Twitter i wielu innych - i możesz zrobić to WSZYSTKO w tym samym czasie z tego SAMEGO pojedynczego obiektu - który jest łatwo dostępny, w znanym stanie w dowolnym momencie w dowolnym kodzie, do którego go podłączysz. Możesz nawet utworzyć wiele sesji z wieloma różnymi interfejsami API / usługami innych firm dla tego SAMEGO użytkownika we własnej witrynie / aplikacji i robić z nimi co chcesz.

Oczywiście, wszystko to może być również tonowane tradycyjnymi metodami przy użyciu normalnych klas i obiektów - haczyk polega na tym, że singleton jest uporządkowany, schludniejszy i dlatego łatwiejszy w zarządzaniu / testowaniu w porównaniu z tradycyjnym użyciem klas / obiektów w takich sytuacjach.

- Ludzie, którzy potrzebują szybkiego rozwoju

Globalne zachowanie singletonów ułatwia budowanie dowolnego kodu za pomocą frameworka, który ma kolekcję singletonów do zbudowania, ponieważ gdy dobrze skonstruujesz swoje klasy singleton, ustalone, dojrzałe i ustawione metody będą łatwo dostępne i do użytku w dowolnym miejscu i czasie w spójny sposób. Dojrzewanie twoich klas zajmuje trochę czasu, ale potem są solidne, spójne i przydatne. Możesz mieć dowolną liczbę metod w singletonie robiącym, co chcesz, i chociaż może to zwiększyć ślad pamięci obiektu, przynosi znacznie więcej oszczędności czasu wymaganego do szybkiego rozwoju - metody, której nie używasz w jednej konkretnej instancji aplikacja może być używana w innej zintegrowanej aplikacji i możesz po prostu wcisnąć nową funkcję, o którą poprosi klient / szef / kierownik projektu, wprowadzając tylko kilka modyfikacji.

Masz pomysł. Przejdźmy teraz do zarzutów wobec singletonów i bezbożnej krucjaty przeciwko czemuś, co jest przydatne :

- Najważniejszym zarzutem jest to, że utrudnia to testowanie.

I naprawdę, do pewnego stopnia, nawet jeśli można to łatwo złagodzić, stosując odpowiednie środki ostrożności i kodując procedury debugowania w swoich singletonach, ZE świadomością, że będziesz debugować singleton. Ale widzisz, to nie różni się zbytnio od JAKIEJKOLWIEK innej filozofii / metody / wzorca kodowania, która istnieje - po prostu singletony są stosunkowo nowe i nie są rozpowszechnione, więc obecne metody testowania kończą się porównywalnie z nimi niekompatybilnymi. Ale to nie różni się w żadnym aspekcie języków programowania - różne style wymagają innego podejścia.

Jedna kwestia jest beznadziejna, ponieważ pomija fakt, że aplikacje nie są przeznaczone do „testowania”, a testowanie nie jest jedyną fazą / procesem, który obejmuje tworzenie aplikacji. Aplikacje są opracowywane do użytku produkcyjnego. I jak wyjaśniłem w sekcji „kto potrzebuje singletonów”, singletony mogą wyciąć WIELKĄ ofertę ze złożoności związanej z koniecznością tworzenia kodu działającego WEWNĄTRZ wielu różnych baz kodu / aplikacji / usług stron trzecich. Czas, który można stracić na testowaniu, to czas poświęcony na rozwój i wdrożenie. Jest to szczególnie przydatne w dobie uwierzytelniania / aplikacji / integracji stron trzecich - Facebook, Twitter, OpenID i wiele innych i kto wie, co dalej.

Choć jest to zrozumiałe - programiści pracują w bardzo różnych okolicznościach w zależności od ich kariery. I dla osób, które pracują w stosunkowo dużych firmach z określonymi działami, obsługującymi różne, zdefiniowane oprogramowanie / aplikacje w wygodny sposób i bez zbliżającej się klęski cięć budżetowych / zwolnień i towarzyszącej im potrzeby robienia WIELU rzeczy z wieloma różnymi rzeczami w tania / szybka / niezawodna moda, singletony mogą wydawać się niepotrzebne. I może to być nawet uciążliwe / przeszkodą w tym, co już mają.

Ale dla tych, którzy muszą pracować w brudnych okopach zwinnego rozwoju, zmuszając do wdrażania wielu różnych żądań (czasem nieracjonalnych) od swojego klienta / menedżera / projektu, singletony są zbawieniem z powodów wyjaśnionych wcześniej.

- Kolejnym zarzutem jest to, że zużycie pamięci jest większe

Ponieważ dla każdego żądania od każdego klienta będzie istniał nowy singleton, MOŻE to być zastrzeżenie dla PHP. W przypadku źle skonstruowanych i używanych singletonów, zużycie pamięci aplikacji może być większe, jeśli w danym momencie aplikacja obsługuje wielu użytkowników.

Chociaż jest to ważne dla KAŻDEGO rodzaju podejścia, które możesz zastosować podczas kodowania rzeczy. Pytanie, które należy zadać, brzmi: czy metody, dane, które są przechowywane i przetwarzane przez te single, są niepotrzebne? Jeśli są one niezbędne w wielu żądaniach, które otrzymuje aplikacja, to nawet jeśli nie używasz singletonów, te metody i dane BĘDĄ obecne w Twojej aplikacji w takiej czy innej formie za pośrednictwem kodu. Tak więc wszystko staje się kwestią tego, ile pamięci zaoszczędzisz, kiedy zainicjujesz tradycyjny obiekt klasy 1/3 do przetwarzania kodu i zniszczysz go w 3/4.

Widzisz, postawione w ten sposób, pytanie staje się zupełnie nieistotne - nie powinno być zbędnych metod, danych przechowywanych w obiektach w KODZIE ŻADNYCH - niezależnie od tego, czy używasz singletonów, czy nie. Tak więc ten sprzeciw wobec singletonów staje się naprawdę zabawny, ponieważ ZAKŁADA, że w obiektach utworzonych z klas, których używasz, będą niepotrzebne metody, dane.

- Niektóre nieprawidłowe zastrzeżenia, takie jak `` uniemożliwiają / utrudniają utrzymanie wielu połączeń z bazą danych ''

Nie mogę nawet zacząć rozumieć tego zarzutu, gdy wszystko, czego potrzeba do utrzymania wielu połączeń do bazy danych, wielu wyborów do bazy danych, wielu zapytań do bazy danych, wiele zestawów wyników w danym singletonie, to po prostu trzymanie ich w zmiennych / tablicach w singletonie tak długo, jak długo są potrzebne. Może to być tak proste, jak trzymanie ich w tablicach, chociaż możesz wymyślić dowolną metodę, której chcesz użyć, aby to osiągnąć. Spójrzmy jednak na najprostszy przypadek, użycie zmiennych i tablic w danym singletonie:

Wyobraź sobie, że poniżej znajduje się singleton danej bazy danych:

$ this -> połączenia = tablica (); (zła składnia, po prostu wpisałem to w ten sposób, aby dać ci obraz - właściwa deklaracja zmiennej to public $ connections = array (); a jej użycie to $ this-> connections ['connectionkey'] naturalnie)

W ten sposób możesz skonfigurować i utrzymywać wiele połączeń w dowolnym momencie w tablicy. To samo dotyczy zapytań, zestawów wyników i tak dalej.

$ this -> query (QUERYSTRING, 'queryname', $ this-> connections ['cząstkowe połączenie']);

Który może po prostu wykonać zapytanie do wybranej bazy danych z wybranym połączeniem i po prostu zapisać w pliku

$ this -> wyniki

tablica z kluczem „queryname”. Oczywiście będziesz musiał mieć zakodowaną metodę zapytania - co jest trywialne.

Umożliwia to utrzymywanie praktycznie nieskończonej liczby (na tyle, na ile pozwalają na to limity zasobów) różnych połączeń z bazą danych i zestawów wyników w takim stopniu, w jakim ich potrzebujesz. I są one dostępne dla KAŻDEGO fragmentu kodu w dowolnym punkcie w dowolnej bazie kodu, w której została utworzona instancja tej klasy pojedynczej.

Oczywiście naturalnie musiałbyś zwolnić zestawy wyników i połączenia, gdy nie są potrzebne - ale to oczywiste, i nie jest to specyficzne dla singletonów ani żadnej innej metody / stylu / koncepcji kodowania.

W tym momencie możesz zobaczyć, jak możesz utrzymywać wiele połączeń / stanów z aplikacjami lub usługami innych firm w tym samym singletonie. Nie tak różne.

Krótko mówiąc, w końcu pojedyncze wzorce są po prostu kolejną metodą / stylem / filozofią do programowania i są tak samo przydatne jak KAŻDE inne, gdy są używane we właściwym miejscu, we właściwy sposób. Co nie różni się od niczego.

Zauważysz, że w większości artykułów, w których atakuje się singletony, zobaczysz również odniesienia do „globalnych” będących „złymi”.

Spójrzmy prawdzie w oczy - wszystko, co nie jest używane prawidłowo, nadużywane, niewłaściwie używane, JEST złe. Nie ogranicza się to do żadnego języka, żadnej koncepcji kodowania, żadnej metody. Ilekroć zobaczysz, że ktoś wydaje ogólne stwierdzenia, takie jak „X jest zły”, uciekaj od tego artykułu. Szanse są bardzo duże, że jest to efekt ograniczonego punktu widzenia - nawet jeśli punkt widzenia jest wynikiem wieloletnich doświadczeń w czymś konkretnym - co generalnie kończy się nadmierną pracą w danym stylu / metodzie - typowym konserwatyzmem intelektualnym.

Można na to podać niezliczone przykłady, od „globalne są złe” po „iframe są złe”. Około 10 lat temu nawet proponowanie użycia iframe w dowolnej aplikacji było herezją. Potem pojawia się Facebook, ramki iframe wszędzie i zobacz, co się stało - ramki iframe nie są już takie złe.

Wciąż są ludzie, którzy uparcie twierdzą, że są „źli” - a czasem też z dobrego powodu - ale, jak widać, istnieje potrzeba, elementy iframe wypełniają tę potrzebę i działają dobrze, a zatem cały świat po prostu się przemieszcza.

Największą zaletą programisty / kodera / inżyniera oprogramowania jest wolny, otwarty i elastyczny umysł.

jedność100
źródło
2
-1. Chociaż zgadzam się, że posiadanie otwartego i elastycznego umysłu jest niezbędnym atutem dla każdego programisty, nie zwalnia to Singletona od bycia Antypatem. Powyższa odpowiedź zawiera tak wiele niedokładnych stwierdzeń i błędnych wniosków na temat natury i skutków działania Singletona, że ​​nie mogę go nie zgodzić.
Gordon
-1. Musiałem osobiście doświadczyć frameworka z wieloma singletonami i automatyczne testowanie jest niemożliwe. Muszę wszystko ręcznie przetestować metodą prób i błędów w przeglądarce. Niektórym błędom można zapobiec podczas przeglądu kodu (błędy pisowni, błędy składniowe), ale błędy funkcjonalne są często ukryte. To testowanie wymaga znacznie więcej czasu niż testy jednostkowe. Przy testach jednostkowych mógłbym powiedzieć: ta klasa działa w izolacji, błąd musi być gdzie indziej. Bez debugowania jest żmudne.
Jim Martens
Framework musiał mieć wbudowane logowanie i śledzenie błędów. Ponadto klasa działająca poprawnie w izolacji również działałaby poprawnie w postaci pojedynczej, gdy zostanie umieszczona w szerszej aplikacji. Co oznacza, że ​​w tym przypadku to, co się psuje, to inna klasa lub funkcja, która oddziałuje z tym singletonem. Nie różni się to niczym od zwykłego śledzenia błędów w dużej aplikacji. Co samo w sobie jest dość trudne bez odpowiedniego logowania aplikacji.
jedność100
Niedokładny. Mnóstwo singletonów jest zdecydowanie ZŁE, ponieważ tworzy Testing-HELL. :-) Jednak jeden singletone na aplikację może być dobry. Na przykład: jako ujednolicona funkcja rejestrowania - do wdrożenia we wszystkich aplikacjach (w tym niektórych starszych kodach).
Filip OvertoneSinger Rydlo
„Czas, który można stracić na testowaniu…” To naprawdę bardzo zła praktyka i sposób myślenia. Wszystkie te starsze aplikacje zostały opracowane z myślą o tym i utrzymanie ich stało się niemożliwe, więc musiały zostać przepisane. Jeśli nie ma testów, czas zostanie utracony, gdy zostanie opracowana nowa funkcja i zepsuje coś w innej części systemu. Czas stracony na debugowanie, czas tracony przez użytkowników, którzy mogą korzystać z tej funkcji prawidłowo, zaufanie w aplikacji stracił itd
bogdancep
15

Singletony są uważane przez wielu za anty-wzorce, ponieważ są po prostu gloryfikowanymi zmiennymi globalnymi. W praktyce istnieje stosunkowo niewiele sytuacji, w których jest to konieczne dla klasy mieć tylko jedną instancję; zwykle wystarczy, że jedna instancja jest wystarczająca , w którym to przypadku implementacja jej jako singletona jest całkowicie niepotrzebna.

Odpowiadając na pytanie, masz rację, że singletony są tutaj przesadą. Wystarczy prosta zmienna lub funkcja. Lepszym (solidniejszym) podejściem byłoby jednak użycie wstrzykiwania zależności w celu całkowitego wyeliminowania potrzeby stosowania zmiennych globalnych.

Will Vousden
źródło
Ale Singletony mogą bardzo płynnie degradować się do DI, klasy statyczne nie mogą, co jest prawdziwym problemem w przypadku klas statycznych.
Bill K
@Bill: Bardzo prawda, ale z tego względu na początku
zalecałbym
W niektórych językach (takich jak Java) klasy statyczne (lub metody statyczne klas) nie mogą być rozszerzane. Tworzysz więc potencjalne problemy (lub w najlepszym przypadku więcej pracy) dla przyszłych programistów. Dlatego niektórzy sugerują, że generalnie należy unikać metod statycznych, chyba że masz ich konkretną potrzebę.
Marvo
8

W swoim przykładzie masz do czynienia z pojedynczą, pozornie niezmienną informacją. W tym przykładzie Singleton byłby przesadą i samo użycie statycznej funkcji w klasie wystarczy.

Więcej myśli: Możesz doświadczać przypadku wdrażania wzorców ze względu na wzorce, a Twój żołądek mówi ci „nie, nie musisz” z powodów, które określiłeś.

ALE: Nie mamy pojęcia o wielkości i zakresie twojego projektu. Jeśli jest to prosty kod, być może wyrzucony, prawdopodobnie nie będzie wymagał zmiany, to tak, użyj statycznych elementów członkowskich. Ale jeśli uważasz, że twój projekt może wymagać skalowania lub przygotowania do kodowania konserwacyjnego w przyszłości, tak, możesz chcieć użyć wzorca Singleton.

Paul Sasik
źródło
1
Wow, po prostu źle. Cała różnica (odpowiedź na pytanie) polega na tym, o ile trudniej jest później naprawić kod, aby dodać drugą instancję. Jest to o wiele trudniejsze, jeśli używasz metod statycznych. To tak, jakby powiedzieć „Globale są w porządku w twoich ograniczonych warunkach”, kiedy cały problem z Globals polega na tym, że warunki się zmieniają.
Bill K
@Bill K: Zgadzam się z tobą i użyłbym singletona, gdyby w ogóle była jakaś złożoność. Ale próbowałem odpowiedzieć na to pytanie z punktu widzenia PO i pomyślałem, cóż, tak, myślę, że to przesada w tym bardzo ograniczonym przypadku. Oczywiście ignorowałem kwestie architektoniczne lub związane ze skalowalnością oraz mnóstwo innych kwestii. Czy powinienem był zawrzeć to jako zastrzeżenie w mojej odpowiedzi na alo z wyjaśnieniem, dlaczego ktoś powinien zawsze używać singletona ... co z pewnością spowodowałoby negatywne głosy innych?
Paul Sasik
5

Po pierwsze, chcę tylko powiedzieć, że nie znajduję wielu zastosowań dla wzorca Singleton. Dlaczego ktoś miałby chcieć, aby jeden obiekt był w całej aplikacji? Szczególnie w przypadku baz danych, co jeśli chcę połączyć się z innym serwerem bazy danych? Za każdym razem muszę się rozłączać i podłączać ponownie ...? Tak czy siak...

Istnieje kilka wad używania globali w aplikacji (co robi tradycyjne użycie wzorca Singleton):

  • Trudny do testu jednostkowego
  • Problemy z wstrzykiwaniem zależności
  • Może powodować problemy z blokowaniem (aplikacja wielowątkowa)

Używanie klas statycznych zamiast instancji singleton ma również te same wady, ponieważ największym problemem singletona jest getInstancemetoda statyczna .

Możesz ograniczyć liczbę instancji, które może mieć klasa bez korzystania z tradycyjnej getInstancemetody:

class Single {

    static private $_instance = false;

    public function __construct() {
        if (self::$_instance)
           throw new RuntimeException('An instance of '.__CLASS__.' already exists');

        self::$_instance = true;
    }

    private function __clone() {
        throw new RuntimeException('Cannot clone a singleton class');
    }

    public function __destruct() {
        self::$_instance = false;
    }

}

$a = new Single;
$b = new Single; // error
$b = clone($a); // error
unset($a);
$b = new Single; // works

Pomoże to w pierwszych punktach wymienionych powyżej: testowanie jednostkowe i wstrzykiwanie zależności; jednocześnie upewniając się, że w Twojej aplikacji istnieje jedno wystąpienie klasy. Możesz na przykład po prostu przekazać wynikowy obiekt do swoich modeli (wzorzec MVC), aby mogli go użyć.

netcoder
źródło
5

Zastanów się po prostu, czym różni się Twoje rozwiązanie od tego przedstawionego w dokumentacji PHP. W rzeczywistości, jest tylko jedna „mała” różnica: rozwiązanie zapewnia dzwoniących z getter z PDOinstancji, a jeden w docs zapewnia dzwoniących z Database::singletonz Databaseinstancji (oni następnie użyć getter na tym, by uzyskać PDOinstancji).

Więc do jakiego wniosku dochodzimy?

  • W kodzie dokumentacji wywołujący otrzymują Databaseinstancję. DatabaseKlasa może narazić (w rzeczywistości, to powinien wystawiać jeśli masz zamiar do tych wszystkich kłopotów) bogatsze lub wyższym poziomie niż interfejs PDOobiektu jest opakowany.
  • Jeśli zmienisz implementację, aby zwrócić inny (bogatszy) typ niż PDO, te dwie implementacje są równoważne. Postępowanie zgodnie z ręczną implementacją nie przynosi żadnych korzyści.

Z praktycznego punktu widzenia Singleton to dość kontrowersyjny wzór. Dzieje się tak głównie dlatego, że:

  • Jest nadużywany. Początkujący programiści rozumieją Singleton znacznie łatwiej niż inne wzorce. Następnie wykorzystują swoją nowo zdobytą wiedzę wszędzie, nawet jeśli dany problem można rozwiązać lepiej bez Singletona (gdy trzymasz młotek, wszystko wygląda jak gwóźdź).
  • W zależności od języka programowania, zaimplementowanie Singletona w szczelny, nieszczelny sposób może okazać się tytanicznym zadaniem (szczególnie jeśli mamy zaawansowane scenariusze: singleton zależny od innego singletona, singletony, które można zniszczyć i odtworzyć itp. ). Po prostu spróbuj poszukać „ostatecznej” implementacji Singletona w C ++, ośmielam się (jestem właścicielem przełomowego projektu Andrei Alexandrescu w Modern C ++, który dokumentuje większość bałaganu).
  • Nakłada dodatkowe obciążenie zarówno podczas kodowania Singletona, jak i podczas pisania kodu, aby uzyskać do niego dostęp, obciążenie, bez którego możesz się obejść, przestrzegając kilku narzuconych sobie ograniczeń dotyczących tego, co próbujesz zrobić ze zmiennymi programu.

A więc na koniec: Twój singleton jest w porządku. Nieużywanie Singletona w ogóle jest również w porządku przez większość czasu.

Jon
źródło
2

Twoja interpretacja jest poprawna. Singletony mają swoje miejsce, ale są nadużywane. Często dostęp do statycznych funkcji składowych jest wystarczający (zwłaszcza, gdy nie trzeba w żaden sposób kontrolować czasu budowy). Lepiej, możesz po prostu umieścić kilka wolnych funkcji i zmiennych w przestrzeni nazw.

Lekkość wyścigów na orbicie
źródło
2

Podczas programowania nie ma „dobrych” i „złych”; istnieje „dobra praktyka” i „zła praktyka”.

Singletony są generalnie tworzone jako klasy do późniejszego ponownego wykorzystania. Muszą być stworzone w taki sposób, aby programista nie utworzył przypadkowo dwóch instancji podczas pijackiego kodowania o północy.

Jeśli masz prostą małą klasę, której instancja nie powinna być tworzona więcej niż raz, nie musisz tworzyć jej pojedynczej klasy. Jeśli tak, to tylko siatka bezpieczeństwa.

posiadanie obiektów globalnych nie zawsze jest złą praktyką. Jeśli wiesz, że będziesz go używać globalnie / wszędzie / cały czas, może to być jeden z nielicznych wyjątków. Jednak osoby globalne są ogólnie uważane za „złą praktykę” w taki sam sposób, jak gotoza złą praktykę.

zzzzBov
źródło
2

Nie widzę w tym żadnego sensu. Jeśli zaimplementowałeś klasę w taki sposób, że ciąg połączenia został przyjęty jako parametr do konstruktora i utrzymywałeś listę obiektów PDO (po jednym dla każdego unikalnego ciągu połączenia), to być może byłoby jakieś korzyści, ale implementacja singletona w ten przykład wydaje się bezcelowym ćwiczeniem.

Kell
źródło
1

O ile widzę, niczego Ci nie brakuje. Przykład jest dość błędny. Miałoby to znaczenie, gdyby klasa pojedyncza miała jakieś niestatyczne zmienne instancji.

FooLman
źródło