Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Singleton a inne rozwiązania
Forum PHP.pl > Forum > PHP > Object-oriented programming
wookieb
Od ostatniego czasu zauważam wielką nagonkę na Singletona. Aż czasem głupio mi poradzić komuś w temacie by użył singletona bo zaraz pod spodem sypią sie posty typu
"Singleton ssie",
"Singleton jest dla leniwych",
"Singleton to zło" i podobne.

Osobiście nie rozumiem, dlaczego ludzie tak bardzo na to naskakują.

W swoim "frameworku" używam singletona dość często z jednego ważnego względu. Mam sporą ilość klas, których konieczne jest istnienie jednego egzemplarza.

Oto przykładowe klasy.

Site:
  • Przechowuje wszystkie ustawienia dotyczące działania systemu (znak separacjie urla i sporo takich typowo systemowych)
  • do niej importuje listę plików jakie należy dołączyć w htmlu w sekcji head
  • posiada egzemplarz klasy do obsługi URL (tworzenie, parsowanie, analiza)
  • Komunikaty dla użytkownika
  • tutaj pare innych mniej waznych funkcji
Nie widzę innego rozwiązania jak singleton. Nie wyobrazam sobie trzymania wartości klasy jako statycznych i za kazdym razem odwoływania sie do tej klasy poprzez
  1. <?php
  2. $site=new Site();
  3. ?>


Baza danych:
Tutaj chyba nie trzeba mówić, co owa klasa czyni. Rozumiem argument "A co jeżeli chcesz utworzyć połączenie z nową bazą danych?"
Można to rozwiązać bez najmniejszego problemu (np. utworzenie klasy dbConnection). Lecz w większości przypadków korzysta się z jednej bazy i jednego pola.
Singleton jak najbardziej na +;

System szablonów:
Rozszerzenie Open Power Template, gdzie dodałem singletona. Dlaczego? Bez sensu jest za każdym razem tworzenie nowego egzemplarza nawet jak chce przeparsować tylko małą część strony, niepotrzebne użycie pamięcie poprzez wartości każdego nowego egzemplarza.
Singleton + za mniejsze zużycie pamięci.

Użytkownik:
Całą gama funkcji operujących na użytkowniku, wylogowanie, logowanie, zmiana danych, zalanie herbaty, masaż i wiele innych.
Przechowuje wszystkie informacje o użytkowniku, Od jego id po preferencje, do uprawnień.
Singleton +. Jeden użytkownik jeden obiekt.

Chciałbym teraz wiedzieć jakie są argumenty osób, tak bardzo nienawidzących singletona, przemawiające za tym żeby nie używać singletona w owych sytuacjach albo wskazać inne rozwiązania które owy problem rozwiążą.

Przyznam, że nie jestem super specem od znajomości mnóstwa Wzorców Projektowych. Chętnie poznam nowe rozwiązania i pomysły które oświecą i zbeszczeszczą moje dobre zdanie o singletonie smile.gif .

Rozwiązania które nie wchodzą w grę:
global - bron boze jak gdzies ponadpisuje egzemplarze klas
przekazywanie do kazdego obiektu wskaznika do tych klas - masochistą nie jestem aby każdy mój konstruktor składał się z bogatej listy argumentów.
Crozin
Cytat
Od ostatniego czasu zauważam wielką nagonkę na Singletona.
Bo z reguły poleca się go w momencie, gdy dostęp do jakiejś klasy ma być globalny (chociaż często nawet nie jest on gkobalny). A singleton powstał by zagwarantować tylko jedną instancję danej klasy.
kbsucha
Cytat(wookieb @ 7.06.2009, 23:27:51 ) *
Od ostatniego czasu zauważam wielką nagonkę na Singletona. Aż czasem głupio mi poradzić komuś w temacie by użył singletona bo zaraz pod spodem sypią sie posty typu
"Singleton ssie",
"Singleton jest dla leniwych",
"Singleton to zło" i podobne.

Osobiście nie rozumiem, dlaczego ludzie tak bardzo na to naskakują.

W swoim "frameworku" używam singletona dość często z jednego ważnego względu. Mam sporą ilość klas, których konieczne jest istnienie jednego egzemplarza.


No dobrze, ale co z tego ze sypia posty. Przecież nikt Cię do niczego nie zmusza. Preferujesz Singletona no to czemu masz go nie stosować, ktoś inny lubi Context no to sobie go implementuje w aplikacji Jak znam życie to jeszcze wiele osób używa globali i jakoś sobie radzą, zresztą nie jedną porządną aplikacje zbudowano na globalu i jakoś świat się nie zawalił.

Ja od dłuższego czasu podchodze do tego z większym dostansem i jakoś lepiej mi się żyje guitar.gif
LBO
Widzisz, tu nie chodzi do końca o to gdzie się singleton nadaje najlepiej, ale o to co ze sobą niesie.

Do wad zaliczam:
1. Testowanie singletona to masakra.
2. Singleton ukrywa zależności pomiędzy obiektami (nie są to puste słowa, stoi to później na przeszkodzie w rozwijaniu aplikacji)
3. Dziedziczenie, a singleton.... wolne żarty.
wookieb
Cytat(kbsucha @ 7.06.2009, 23:46:31 ) *
No dobrze, ale co z tego ze sypia posty. Przecież nikt Cię do niczego nie zmusza. Preferujesz Singletona no to czemu masz go nie stosować, ktoś inny lubi Context...

Dlatego pytałem się o propozycje lepszych rozwiązań moich przykładowych zagadnień.
dr_bonzo
Cytat(LBO @ 8.06.2009, 00:13:31 ) *
Widzisz, tu nie chodzi do końca o to gdzie się singleton nadaje najlepiej, ale o to co ze sobą niesie.

Do wad zaliczam:
1. Testowanie singletona to masakra.
2. Singleton ukrywa zależności pomiędzy obiektami (nie są to puste słowa, stoi to później na przeszkodzie w rozwijaniu aplikacji)
3. Dziedziczenie, a singleton.... wolne żarty.


@LBO: to prosze, pokaz jak rozwiazujesz problem modelu (przyklad uzycia), ktory korzysta z bazy danych.
erix
Cytat
przekazywanie do kazdego obiektu wskaznika do tych klas - masochistą nie jestem aby każdy mój konstruktor składał się z bogatej listy argumentów.

Zawsze zostaje func_get_args" title="Zobacz w manualu PHP" target="_manual... Ale to nie zmienia faktu, że trzeba uwzględnić jakąś warstwę pobierającą uchwyt.

Ja byłbym za jednym obiektem "rdzenia" z poszczególnymi modułami ukrytymi wewnątrz. A rdzeń można albo przekazać jako zmienną w parametrze, albo jako własność klasy (jeśli chodzi o model; konstruktor modelu tworzy instancję z ustawionym wskaźnikiem rdzenia).

Nie mam też nic przeciwko Singletonowi, jeśli jest stosowany z głową; nie popadajmy w paranoję, że forma ważniejsza od treści, albo tylko dlatego, że jest to sposób na obejście global." title="Zobacz w manualu PHP" target="_manual. Po to jest rozum, żeby rozgraniczyć. ;]
LBO
Cytat
@LBO: to prosze, pokaz jak rozwiazujesz problem modelu (przyklad uzycia), ktory korzysta z bazy danych.


Mam fabrykę modeli w której wstrzykuje kontekst.

[nowy post]

Cytat(wookieb @ 7.06.2009, 23:27:51 ) *
przekazywanie do kazdego obiektu wskaznika do tych klas - masochistą nie jestem aby każdy mój konstruktor składał się z bogatej listy argumentów.


Wstrzykiwanie nie odbywa się tylko poprzez konstruktor, czasami wystarczą tylko settery. Poczytaj o kontenerach IoC np w Springu.
mike
Ja chciałbym się przyłączyć do ~LBO żeby nie wyszło że prowadzi samotną krucjatę.
Uwierzcie, że istnieją inne, dużo lepsze sposoby na rozwiązywanie wspomnianych problemów.

Odwrócenie sterowania (IoC) za pomocą wstrzykiwania zależności (ang. Dependency Injection, DI) jest dużo lepszym rozwiązaniem i wcale nie opiera się na globalnych obiektach tworzonych za pomocą Singletona.
Zapraszam do lektury: Inversion of Control Containers and the Dependency Injection pattern autorstwa Martina Fowlera

Poza tym nikt nie odniósł się do wad wspomnianych przez ~LBO. W światku "script kids by PHP" testowanie i projektowanie to też poważne problemy i zagadnienia.

P.S.
~LBO Twoja sygnaturka mnie powaliła. Git.
starach
A co w takim razie z menadżerem bazy danych. Skoro klasy Peer są statyczne ( czy to Propel czy nawet mój miniORM ) to jak mam wywoływać DatabaseManager, która to metoda instance() pobiera z konfiguracji odpowiednie ustawienia bazy i zwraca już połączony sterownik. Chyba mniej więcej działa tak Doctrine.

Generalnie to niby nie jest singleton tylko factory, tylko że zasada działania pozostaje bardzo zbliżona do pierwszego.
wookieb
Strasznie nie podoba mi się wstrzykiwanie wskaźników do klas z tego wzgledu, ze jest to bardzo niewygodne.
A juz organizacja wstrzykiwan przez xmle (o ile dobrze zrozumiałem "na szybko") jest głupotą.

Problem rozwiązałem w trochę inny sposób. Mianowicie statyczna klasa
  1. <?php
  2. /**
  3. *    Statyczna klasa służąca jako obejście dla singletona. Przechowuje egzemplarze klas, których potrzebny jest tylko jeden egzemplarz.
  4. *    @author Wookieb
  5. *    @version 1.0
  6. *    @package Utils
  7. */
  8.  
  9. class GlobalHandler
  10. {
  11.    /**
  12.     *    Przechowuje egzemplarze klas
  13.     *    @var array
  14.     */
  15.    private static $instances=array();
  16.    
  17.    /**
  18.     *    Zapisuje do przechowania
  19.     *    @param string $name alias pod jakim zapisac dane
  20.     *    @param mixed $var dane do przechowania
  21.     */
  22.    public static function register($name, $var)
  23.    {
  24.        self::$instances[$name]=$var;
  25.    }
  26.    /**
  27.     *    Usuwa przechowane dane o podanej nazwie
  28.     *    @param string $name alias danych
  29.     *    @return bool true jezeli taki alias istnieje, false jezeli nie
  30.     */
  31.    public static function unregister($name)
  32.    {
  33.        if(isset(self::$instances[$name]))
  34.        {
  35.            unset(self::$instances[$name]);
  36.            return true;
  37.        }
  38.        else return false;
  39.    }
  40.    /**
  41.     *    Zwraca dane o podanym aliasie
  42.     *    @param string $name alias danych
  43.     *    @return mixed
  44.     */
  45.    public static function get($name)
  46.    {
  47.        if(isset(self::$instances[$name]))
  48.        {
  49.            return self::$instances[$name];
  50.        }
  51.        else return false;
  52.    }
  53.    
  54.    /**
  55.     *    Usuwa wszystkie przechowane dane
  56.     */
  57.    public static function unregisterAll()
  58.    {
  59.        self::$instances=array();
  60.    }
  61. }
  62. ?>

Sądze ze działanie jest na tyle jasne, że niczego nie trzeba tłumaczyć.

Musiałem wywalić wszystkie singletony z tego względu, że będę chciał czasem symulować stronę w stronie (oczywiście nie za pomocą iframe).

Problem różnych połączeń z bazą danych rozwiązałem w ten sposób iż utworzyłem klasę DbConnection która realizuje połączenie. Egzemplarz owej klasy przekazuje do głównej klasy zarządzającej zapytaniami (Db). W razie potrzeby połączenia z inną bazą, po prostu tworze nowy egzemplarz DbConnection i na jakiś czas zamieniam w Db. Działa bardzo sprawnie i wydajnie.

Dzięki wszystkim za sugestie smile.gif
LBO
Cytat(mike @ 8.06.2009, 10:13:44 ) *
Ja chciałbym się przyłączyć do ~LBO żeby nie wyszło że prowadzi samotną krucjatę

@mike, dzięki wielkie za wsparcie - jak zwykle, z Twojej strony, trafnie i konkretnie. Naprawdę bardzo doceniam.

Cytat(mike @ 8.06.2009, 10:13:44 ) *
P.S.
~LBO Twoja sygnaturka mnie powaliła. Git.

tongue.gif Starałem się

Cytat(wookieb @ 11.06.2009, 22:37:59 ) *
Strasznie nie podoba mi się wstrzykiwanie wskaźników do klas z tego wzgledu, ze jest to bardzo niewygodne.
A juz organizacja wstrzykiwan przez xmle (o ile dobrze zrozumiałem "na szybko") jest głupotą.

Głupotą? A możesz poprzeć to jakimiś argumentami?

Bo ja dzięki tej głupocie, osiągam tyle samo co przy użyciu Singletona, ale bez jego przykrych naleciałości.

Co do twojego rozwiązania. Wiele to od Singletona nie odbiega. Teraz masz Rejestr w czystej jego postaci - drugi, zaraz po Singletonie, niezbyt ciekawy pod względem inżynieryjnym wzorzec.
Na blogu Federico Cargneluttiego - bardzo cenię sobie tego autora, mnóstwo ciekawych artykułów m.in. o wzorcach - znajdziesz kilka słów na ten temat.

Mam ogromną nadzieję, że po wydaniu Symfony 2.0 wraz z autorskim kontenerem IoC programiści zaczną wnikliwiej przyglądać się pewnym problemom.

Pozdrawiam, Alan
wookieb
Cytat(LBO @ 11.06.2009, 23:51:30 ) *
Głupotą? A możesz poprzeć to jakimiś argumentami?

Nie wyobrażam sobie aby przy tworzeniu instancji obiektu musiała sobie parsować xml-a, żeby działała. Strata czasu procesora.
Cytat
Bo ja dzięki tej głupocie, osiągam tyle samo co przy użyciu Singletona, ale bez jego przykrych naleciałości.

No widzisz. Ja rozwiązałem w swój sposób. Idealny do mojego rozwiązania. W razie potrzeby usuwam wszystkie przechowane dane i wrzucam drugie. Rozwiązanie szybkie, wydajne, nie marnujące pamięci.

Cytat
Co do twojego rozwiązania. Wiele to od Singletona nie odbiega. Teraz masz Rejestr w czystej jego postaci - drugi, zaraz po Singletonie, niezbyt ciekawy pod względem inżynieryjnym wzorzec.
Na blogu Federico Cargneluttiego - bardzo cenię sobie tego autora, mnóstwo ciekawych artykułów m.in. o wzorcach - znajdziesz kilka słów na ten temat.

Twoje zdanie, nie tępie.


Każde rozwiązanie ma swoje wady i zalety. Ja potrzebowałem łatwego, szybkiego sposobu do uzyskiwania dostepu do egzemplarza klasy bez singletona. Rozwiązało to mój problem i dla mnie jest to koniec tematu.
LBO
Cytat(wookieb @ 12.06.2009, 09:31:39 ) *
Nie wyobrażam sobie aby przy tworzeniu instancji obiektu musiała sobie parsować xml-a, żeby działała. Strata czasu procesora.


W dobrych kontenerach, konfiguracja parsowana jest tylko raz i kompilowana do czystego PHP. Narzutu więc nie ma, bo suma sumarum taki kontener generuje to samo, co ty byś napisał z palca (w pewnym stopniu abstrakcji ofkorz).

Cytat(wookieb @ 12.06.2009, 09:31:39 ) *
No widzisz. Ja rozwiązałem w swój sposób. Idealny do mojego rozwiązania. W razie potrzeby usuwam wszystkie przechowane dane i wrzucam drugie. Rozwiązanie szybkie, wydajne, nie marnujące pamięci.


Nie marnujące pamięci? W rejestrze trzymasz cały zestaw zainstancjonowanych obiektów i jest tak nawet, gdy nie używasz połowy z Nich. Kontenery, dzięki konfiguracji, są zdolne tworzyć obiekty dopiero na żądanie.
wookieb
Mylisz się. Wrzucam tylko te które używam i których konieczne jest istnienie chociaż jednego egzemplarza. Nie sprawdzisz tego więc nie możesz z góry zakładać, że ich nie używam.
rzymek01
Ze swojej strony chciałbym się dowiedzieć w jaki sposób zastąpić Singletona (czy trzeba?) w realizacji swoistego controllera (przetwarza eventy i wywołuje odpowiednie moduły) który musi mieć tylko 1 instancję?

Pozdrawiam
Fifi209
@up
Ta klasa rejestruje zmienne, usuwasz singletona w swojej klasie. W tej rejestrujesz obiekt i gotowe. haha.gif

@edit

  1. <?php
  2. public static function get($name)
  3.   {
  4.       if(isset(self::$instances[$name]))
  5.       {
  6.           return self::$instances[$name];
  7.       }
  8.       else return false;
  9.   }
  10. ?>


A czemu tak? biggrin.gif Nie lepiej użyć "magicznych" metod php 5?
__get()

P.S. Ta klasa może służyć też do przechowywania zwykłych zmiennych - np. zamiast używania global.
wookieb
Cytat(fifi209 @ 12.06.2009, 12:39:36 ) *
A czemu tak? biggrin.gif Nie lepiej użyć "magicznych" metod php 5? __get()

Nie bo tak jest znacznie prosciej

Cytat(fifi209 @ 12.06.2009, 12:39:36 ) *
P.S. Ta klasa może służyć też do przechowywania zwykłych zmiennych - np. zamiast używania global.

Nikt nie powiedział, że to ma być wyłącznie do obiektów.
Fifi209
Cytat(wookieb @ 12.06.2009, 13:20:31 ) *
Nie bo tak jest znacznie prosciej

Czemu uważasz, że jest prościej? Zawsze to kilka znaków mniej do wyklikania.

Cytat(wookieb @ 12.06.2009, 13:20:31 ) *
Nikt nie powiedział, że to ma być wyłącznie do obiektów.


Jak nie? biggrin.gif
Cytat
Statyczna klasa służąca jako obejście dla singletona. Przechowuje egzemplarze klas, których potrzebny jest tylko jeden egzemplarz
wookieb
Cytat(fifi209 @ 12.06.2009, 13:23:07 ) *
Czemu uważasz, że jest prościej? Zawsze to kilka znaków mniej do wyklikania.

Bo __get chyba nie działa na statycznych właściwościach


Cytat(fifi209 @ 12.06.2009, 13:23:07 ) *
Jak nie? biggrin.gif


A widzisz tam synonim słowa "wyłącznie"?
Fifi209
Cytat(wookieb @ 12.06.2009, 13:29:23 ) *
Bo __get chyba nie działa na statycznych właściwościach

Wybacz tego nie zabrałem pod uwagę. ;d

Cytat(wookieb @ 12.06.2009, 13:29:23 ) *
A widzisz tam synonim słowa "wyłącznie"?


To jest bezsensowne tłumaczenie, to jakbym powiedział że auto może mieć każdy (a jeździć tylko ten z prawo jazdy)
Przecież nie jest nigdzie napisane: "aby zakupić auto należy mieć prawo jazdy" smile.gif

Mam nadzieję, że mnie rozumiesz. ;>
ernie242
To może być dobra wiadomość:
Cytat
Starting with PHPUnit 4.0, there will be support for running each test in a separate PHP process. Among other benefits, this will allow for testing singletons in an isolated environment.


Z bloga twórcy PHPUnit
wookieb
Pozwolę sobię odświeżyć temat.
Ostatecznie wybrałem jednak metodę Invertion Of Control, która rzeczywiście jest co prawda bardziej pracochłonna ale przynosi bardzo fajne korzyści jeżeli chodzi o zużycie pamięci (nie zawsze są potrzebne wszystkie obiekty) a ja na tym punkcie jestem dość uczulony.

Dodatkowo chciałbym podrzucić ciekawy link mówiący o "wyciekach" pamięci przy używaniu obiektów http://paul-m-jones.com/?p=262
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.