Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Singleton - O Co W Tym Biega?
Forum PHP.pl > Forum > PHP
gkeb
W paru miejscach forum i nie tylko bylo pisane o wzorcu singleton ale jakos nigdzie nie znalazlem wiekszej informacji o tym. Czy ktos moglby podac jakies linki dotyczace tego tematu?? Albo napisze o co w tym biega smile.gif
Dabroz
A ja mam pytanie: czy naprawdę Google trzyma swoje zasoby tylko dla wybranych?
gkeb
szukalem na google wiec nie miej pretensji. nie znalazlem ZADNEGO linka odnoszacego sie do polskich stron traktujacych o tym. Z angielskim u mnie tak sobie smile.gif
Dravo
Witaj
Rada: poprzegladaj przykladowe kody z wykorzystaniem singletona to odrazu zakapujesz o co chodzi, wystarczy chwilka skupienia.
A ja tylko powiem ze uzywanie ich w php5 nie jest konieczne... Jesli nie wiesz jakie zmiany zaszly w php5 to proponuje angielski tekst, ale poparty wieloma przykladami, znajdujacy sie gdzies na stronie PHP5 zenda smile.gif. Szukajcie a znajdziecie 8).
PS. Na forum bylo juz kilka razy wyjasniane co to jest singleton, wiec mozesz/powinnienes tez tu poszukac.
Pozdro
Dabroz
Cytat(gkeb @ 2004-08-15 00:31:09)
szukalem na google wiec nie miej pretensji. nie znalazlem ZADNEGO linka odnoszacego sie do polskich stron traktujacych o tym.

A jednak Google trzyma zasoby tylko dla elit...
Cóż... wyniki szukania
I artykuł znaleziony w Google...
gkeb
eee ten mi umknal sad.gif sorki za zamieszanie
Yarecki
Cytat(Dravo @ 2004-08-15 08:25:13)
A ja tylko powiem ze uzywanie ich w php5 nie jest konieczne...

Nie jest konieczne, ale chyba wygodniejsze niż przekazywanie uchwytu obiektu do i z metody innego obiektu.
gkeb
Rozumiem juz istote dzialania biggrin.gif

Mam coś takiego:
  1. <?php
  2. class foo
  3. {
  4.  
  5.  
  6. function &instance()
  7. {
  8. static $instance = false;
  9. if ( $instance === false )
  10. {
  11. $instance =& new foo();
  12. }
  13. return $instance;
  14. }
  15.  
  16. function blah()
  17. {
  18. return &#092;"Działa!\";
  19. }
  20. }
  21.  
  22. class bar
  23. {
  24. function funkcja()
  25. {
  26. $foo =& foo::instance();
  27. return $foo->blah();
  28. }
  29. }
  30.  
  31. $b =& new bar();
  32. echo $b->funkcja();
  33. ?>


i wszystko jest ok, ale mam jeszcze pewne wątpliwości.
W powyższym przykładzie (który notabene znalazłem na forum smile.gif ) klasa główna (foo) jest inicjowana przez klase bar. Gdy próbowałem przenieść wywołanie funkcji instance do konstruktora klasy foo to mi się wszystko wysypało.
Taki był konstruktor:
  1. <?php
  2.  
  3. function foo()
  4. {
  5. $this->&instance();
  6. }
  7.  
  8. ?>

Jak to zrobić prawidłowo?
Czy dobrze rozumuje że każda klasa musi mieć swoja własną funkcje instance, czy też jest możliwość by funkcja instance byla tylko jedna jak i $instance była tablicąquestionmark.gif
Przepraszam że tak mieszam ale single ton jest dla mnie czymś nowym a OOP zajmóje też sie od niedawna, ale chce sie tego nauczyć aarambo.gif
Yarecki
Cytat(gkeb @ 2004-08-21 12:00:33)
  1. <?php
  2.  
  3. function foo()
  4. {
  5. $this->&instance();
  6. }
  7.  
  8. ?>

Drobny błąd. Jeśli już to powinno być &$this->instance(); chociaż i to (&) w tym przypadku nie jest potrzebne bo nie przypisujesz wyniku zadnej zmiennej.

Z tego co pamiętam w manualu jest napisane, żeby nie używać referencji do klasy w konstruktorze tej klasy.

Co do drugiego pytania to można używać tablicy instance tylko w tym wypadku pojawia się problem przekazywania dynamicznej liczby parametrów do konstruktora. Oczywiście da się to obejść, ale trzeba to przemyśleć.
gkeb
Wg. tego co napisalem pare postów wyżej w każdej klasie bym musiał mieć metode "Instance"(Get) by tworzyć singletona. Przy paru klasach to jest jeszcze ok ale im bardziej będzie się rozwijał projekt to może powstać bałagan.
Napisałem coś takiego (działa):
  1. <?php
  2.  
  3. class Singleton {
  4. var $var;
  5.  
  6. function Singleton()
  7. {
  8. echo &#092;"Singleton::Singleton();<br>\";
  9. }
  10.  
  11. function &Get($name)
  12. {
  13. static $inst = array();
  14. $inst[$name] = NULL;
  15.  
  16. if ($inst[$name] == NULL)
  17. $inst[$name] = new $name;
  18.  
  19. return $inst[$name];
  20. }
  21. }
  22. class Singleton2 {
  23. var $var;
  24.  
  25. function Singleton2()
  26. {
  27. echo &#092;"Singleton2::Singleton();<br>\";
  28. }
  29.  
  30.  
  31. }
  32.  
  33. ?>

Ale jak zauważył Yarecki jest problem z przekazywaniem dynamicznej ilości parametrów do konstruktora danej klasy. Czy można to rozwiązac jako przekazanie tablicy do metody Get?? Chodzi mi o coś takiego:
  1. <?php
  2.  
  3. ...
  4. function &Get($name, $param=array())
  5. {
  6. static $inst = array();
  7. $inst[$name] = NULL;
  8.  
  9. if ($inst[$name] == NULL)
  10. $inst[$name] = new $name($param);
  11.  
  12. return $inst[$name];
  13. }
  14. ...
  15.  
  16. ?>

Czy to jest ok?
Yarecki
Cytat(gkeb @ 2004-08-22 10:58:50)
...
Czy to jest ok?

Według mnie, wszystko co działa i spełnia określone wymagania jest ok :-)

Ja używam czegoś takiego:
  1. <?php
  2.  
  3. class Singleton
  4. {
  5. private static $arrInstances;
  6.  
  7. /**
  8.  * Pobiera instancje klasy, którą przekazano w parametrze
  9.  */
  10. public static function getInstanceOf( $strClassName )
  11. {
  12. if( !isset( self::$arrInstances[$strClassName] ) )
  13. {
  14. require_once $strClassName . '.class.php';
  15.  
  16. self::$arrInstances[$strClassName] = new $strClassName;
  17. }
  18.  
  19. return self::$arrInstances[$strClassName];
  20. }
  21. }
  22.  
  23. ?>


Jak na razie nie bawiłem się w dynamiczne przekazywanie parametrów, bo nie miałem klasy, która potrzebowałaby takowych.
matid
Cytat(gkeb @ 2004-08-22 10:58:50)
...
Czy to jest ok?

Właśnie w tym problem, że to raczej nie będzie ok, chyba że we wszystkich klasach pozbędziesz się wielu argumentów na żecz jednego argumentu-tablicy zawierającego inne.
Bo zapis:
  1. <?php
  2. $inst[$name] = new $name($param);
  3. ?>

Powoduje, że pierwszy argument funkcji będzie miał wartość $param.
Yarecki
matid: to mozna rozwiazac np. tak:

  1. <?php
  2. $inst[md5( $name . serialize( $param ) )] = new $name($param);
  3. ?>
matid
Chyba się niezbyt jasno wyraziłem. Chodzi mi o to, że takie coś utworzy klasę, która nie przyjmie po kolei wszystkich argumentów w $param, tylko przyjmie jeden argument $param.
Więc taka funkcja by musiała mieć taką postać:
  1. <?
  2. function test( $arrParam )
  3. {
  4.  print_r( $arrParam ); // zwraca - [0] => parametr_1, [1] => parametr_2, itp.
  5. }
  6. ?>

A nie tak jak normalne funkcje:
  1. <?
  2. function test( $param1, $param2, $param3 )
  3. {
  4. }
  5. ?>

Więc w ten sposób ograniczamy się tylko do tworzenia instancji napisanych przez nas klas, które zamiast przyjmować wielu parametrów przyjmują tylko jedną tablicę z parametrami.
hawk
Cytat(Yarecki @ 2004-08-23 04:24:14)
Ja używam czegoś takiego:

A to akurat IMHO jest fatalne, bo prowadzi do konfuzji oznaczeń. Po co używać nazwy singleton na coś, co zupełnie singletonem nie jest? Akurat jest innym wzorcem, ale to pozostawiam jako ćwiczenie czytelnikom biggrin.gif . Tak jest jeszcze gorzej niż gdyby nazwać tą klasę Qwertyuiop, bo wprowadza ludzi w błąd.
Yarecki
Cytat(hawk @ 2004-08-23 19:48:19)
Po co używać nazwy singleton na coś, co zupełnie singletonem nie jest? Akurat jest innym wzorcem, ale to pozostawiam jako ćwiczenie czytelnikom biggrin.gif .

Trochę pogrzebałem i wydaje mi się, że jest to Object Pool.
gkeb
matid a jaki problem w tym ze do konstruktora przekaże dane w postaci tablicy?? Przecież gdy wiem ile i jakie dane mają byc przekazane to w prosty sposób moge je przekazac jako zwykle zmienne, np:
  1. <?php
  2.  
  3. function test( $arrParam )
  4. {
  5. $this->param0=$arrParam[0];
  6. $this->param1=$arrParam[1];
  7. }
  8.  
  9. ?>

albo wykorzystując pętle foreach.
Sam konstruktor klasy jest metodą inicjującą obiekt do stanu gotowości.
matid
Cytat(gkeb @ 2004-08-23 22:48:51)
matid a jaki problem w tym ze do konstruktora przekaże dane w postaci tablicy??

Nie ma problemu jeśli korzystasz tylko z własnych klas, natomiast jeśli będziesz miał klasy pisane przez innych to może być konieczność zamieniania kodu danej klasy tak, aby zamiast kilku parametrów przyjmował tylko jedną tablicę. Ja osobiście szukam rozwiązania tego problemu.
hawk
Cytat(Yarecki @ 2004-08-23 21:33:20)
Trochę pogrzebałem i wydaje mi się, że jest to Object Pool.

Yup biggrin.gif. Albo chyba Registry, jak widziałem gdzieś na phppatterns. Implementacje pewnie różne, ale idea ta sama: jeden singleton jako kontener wielu obiektów, które same nie są singletonami.

A z innej beczki, co do parametrów:
Gdzieś widziałem kod prostego Proxy w wykonaniu selkirka, i tam konstruktor proxy przyjmował zmienną liczbę parametrów, sprawdzał ich liczbę i we wielkiej instrukcji case wywoływał konstruktor obiektu bazowego z tą liczbą parametrów co trzeba. Minus - to obsługiwało tylko do 3 parametrów, bo dla każdej liczby trzeba oddzielnie pisać warunek.

A w ogóle to singleton nie powinien mieć parametrów, i już. Kropka. Jak coś przyjmuje parametry, to nie jest to singleton. Bo jak coś ma parametry, to trzeba to gdzieś bezpośrednio stworzyć, a cała idea singletonu polega na tym, że on sam się tworzy i nie obchodzi nas kiedy to się stanie.
gkeb
Cytat
...
A w ogóle to singleton nie powinien mieć parametrów, i już. Kropka. Jak coś przyjmuje parametry, to nie jest to singleton. Bo jak coś ma parametry, to trzeba to gdzieś bezpośrednio stworzyć, a cała idea singletonu polega na tym, że on sam się tworzy i nie obchodzi nas kiedy to się stanie.
...

Hmm, czyli po stworzeniu instancji obiektu dopiero przekazywac do niego potrzebne dane? Czy też musi byc na tyle "samowystarczalny" by sam pobral to co potrzebuje?
matid
Cytat(hawk @ 2004-08-24 23:21:12)
A w ogóle to singleton nie powinien mieć parametrów, i już. Kropka. Jak coś przyjmuje parametry, to nie jest to singleton. Bo jak coś ma parametry, to trzeba to gdzieś bezpośrednio stworzyć, a cała idea singletonu polega na tym, że on sam się tworzy i nie obchodzi nas kiedy to się stanie.

Ale jak np. potrzebujemy wywołać klasę do obsługi bazy danych, która potrzebuje x parametrów, a później chcemy ją wykorzystywać w projekcie i tylko poprzez singleton wywoływać jej instancję to też jest to dobre rozwiązanie. W sumie można to obejść tworząc sobie w klasie Singleton metodę RegisterInstance i normalnie stworzyć sobie obiekt mysql i potem go zarejestrować w singleton'ie.
Ja osobiście jak potrzebuje Singleton z obsługą parametrów to korzystam z tego kodu:

  1. <?php
  2.  
  3. class Singleton
  4. {
  5. static private $arrInstances = array();
  6. static private $strClassesDir = 'classes';
  7.  
  8. /**
  9. * @desc Tworzy instancję klasy
  10. * @access public
  11. **/
  12. static public function Instance( $strClass, $arrParams = array() )
  13. {
  14. if( !in_array( $strClass, self::$arrInstances ) )
  15. {
  16. if( class_exists( $strClass ) )
  17. {
  18. if( isset( $arrParams ) )
  19. {
  20. $strCall = 'self::$arrInstances['' . $strClass . ''] = new ' . $strClass . '( ';
  21. foreach( $arrParams as $param )
  22. {
  23. $strCall .= ''' . $param . '', ';
  24. }
  25. $strCall = substr( $strCall, 0, -);
  26. $strCall .= ' );';
  27. eval( $strCall );
  28. }
  29. else
  30. {
  31. self::$arrInstances[$strClass] = new $strClass();
  32. }
  33. }
  34. else
  35. {
  36. require_once( self::$strClassesDir . '/' . $strClass . '.class.php');
  37. self::$arrInstances[$strClass] = new $strClass;
  38. }
  39. }
  40. return self::GetInstance( $strClass );
  41. }
  42.  
  43. static public function GetInstance( $strClass )
  44. {
  45. if( isset( self::$arrInstances[ $strClass ] ) )
  46. {
  47. if( is_object( self::$arrInstances[ $strClass ] ) )
  48. {
  49. return self::$arrInstances[ $strClass ];
  50. }
  51. else
  52. {
  53. return false;
  54. }
  55. }
  56. else
  57. {
  58. return false;
  59. }
  60. }
  61.  
  62. static public function UnloadInstance( $strClass )
  63. {
  64. if( is_object( self::$arrInstances[ $strClass ] ) )
  65. {
  66. unset( self::$arrInstances[ $strClass ] );
  67. }
  68. else
  69. {
  70. return false;
  71. }
  72. }
  73.  
  74. }
  75.  
  76. ?>

Trochę ten kod pogmatfany, ale działa smile.gif
hawk
Tylko że to już nie jest singleton... Idea podobna ale różnice są spore. Co nie oznacza że jest to złe. Można z tego zrobić np. jakiś inny wzorzec.

Jak już się mówi o wzorcach, to lepiej się ich trzymać, bo wzorce są też sposobem dokumentowania. Jak widzę singleton to wiem że powinienem oczekiwać statycznej metody getInstance() lub podobnej. Jak widzę iterator to szukam next(). Itd, itp.

Używa się wzorców dlatego, że to są pewne sprawdzone rozwiązania, które skracają development time. Ale nazewnictwo klas służy wyłącznie dokumentacji. Takie nazwy jak ArrayIterator, PageFactory, ImageProxy mówią więcej niż strona komentarza. Jeżeli więc zrobię sobie iterator i nazwę go PageFactory, to będzie to szczyt masochizmu i pogardy dla innych. Więc nazywanie singletonem czegoś, co singletonem nie jest, jest Złe przez duże Z.
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.