Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Singleton a klasa "statyczna"
Forum PHP.pl > Forum > PHP > Object-oriented programming
menic
Mogłby ktoś wytłumaczyć, jaka jest własciwie róznica pomiędzy wzorcem Singleton a tym, ze w klasie mamy wszystkie metody statyczne? Bo jakoś nie moge dojśc co i kiedy stosowac. Wg mnie to nie ma w tym wiekszej różnicy. A co wy sądzicie?
Denver
Różnica jest, i to bardzo wyraźna.

Singleton to wzorzec projektowy gwarantujący Ci, że nigdy nie stworzysz więcej, niż jeden obiekt danej klasy - zawsze będziesz korzystać z tego samego egzemplarza. Zapewnia Ci to prywatny konstruktor danej klasy. Klasa ta nie musi mieć tylko i wyłącznie statycznych metod.

Klasa zawierająca same metody statyczne natomiast to niejako zbiornik funkcji - np. klasa String udostępniająca statyczne metody String::ToUpperCase czy też String::RemoveFirstFiveLetters. Klasa ta operuje tylko i wyłącznie na podanych jej metodom argumentach, i nie posiada własnych (chyba, że statycznych). No i nigdy nie tworzysz żadnego ezgemplarza tej klasy.
hwao
Klasa statyczna posiada statyczne metody, generalnie jest jakby "pojemnikiem" często nie powiązanych ze sobą "funkcji" i przede wszystkim nie posiada instancji ( $obiekt = new Cos() ).

Singleton, posiada zainicjowana klase (obiekt), ba nawet więcej gdyż ten wzorzec jest stosowany wtedy gdy chcemy posiadać tylko jedna instancje danej klasy (tj, wszędzie możemy korzystać tylko z tej klasy, nie istnieje możliwość zainicjowania kolejnej).
LBO
Dodam do wypowiedzi @hwao, że wzorzec singletona jest bardzo pomocny w dużych projektach. Znika wtedy problem przekazywania obiektów (kiedyś to był problem (kopie, referencje itp) np. Klasa odpowiedzialna za połączenie z bazą - zazwyczaj potrzebna tylko jedna, bo aplikacja operuje na jednej bazie danych.
menic
Z teoretycznego punktu widzenia rozumiem. Gorzej z praktycznego
hwao
Hm patrz smile.gif

  1. <?php
  2.  
  3. function NewSpace() {
  4. // Jakas nowa przestrzen, tu nie ma zmiennej $Object, z tamtąd
  5. // łapiemy dziada
  6. $Object = Counter::get();
  7.  
  8. echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL;
  9.  
  10. $Object->run();
  11. echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL;
  12.  
  13. }
  14.  
  15. class Counter {
  16. private $i = -1;
  17. private function __construct() {
  18. $this->i = 0;
  19. }
  20.  
  21. private function __clone() {}
  22.  
  23. private static $Instance = null;
  24. public static function get() {
  25. return is_null( self::$Instance ) ? self::$Instance = new Counter() : self::$Instance;
  26. }
  27.  
  28. public function run() {
  29. $this->i++;
  30. }
  31.  
  32. public function getStatus() {
  33. return $this->i;
  34. }
  35. }
  36.  
  37. // Nie darady
  38. //$Object = new Counter();
  39.  
  40. // O, tak lepiej :)
  41. $Object = Counter::get();
  42.  
  43. echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL;
  44.  
  45. $Object->run();
  46. echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL;
  47.  
  48. NewSpace();
  49.  
  50. echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL;
  51.  
  52. ?>

Ciagle operujesz na tym samym obiekcie, nie możesz "powielić".

  1. <?php
  2.  
  3. // Nie darady, prywatny konstrukor
  4. $Object = new Counter();
  5.  
  6. $Object = Counter::get();
  7.  
  8. // Sklonowac dziada tez sie nie da :)
  9. $CloneObject = clone $Object;
  10. ?>


A i rezultat działania (poprawnego).
Kod
Włączony: 0 razy.
Włączony: 1 razy.
Włączony: 1 razy.
Włączony: 2 razy.
Włączony: 2 razy.
menic
Rozumiem smile.gif Ale chodzi mi o róznice w zastosowaniu singletona a statycznych metod. Mam klase która działa na wzorcu singleton oraz działa rownie dobrze z metodami statycznymi. Od czego wiec zalezy jaką technike obrać?
Cysiaczek
Odpowiedź jest niezwykle prosta - od potrzeb : )

Pozdrawiam.
cadavre
Jednak gdy np. połączenia z bazą, w klasie która obsługuje DB nie ma w konstruktorze, a jest ona wywoływana osobną metodą? Wtedy korzystając ze statyków nie nawiązujemy ciągle nowych połączeń.
Cysiaczek
@cadavre - a singletonem niby tak? Połączenie jest ustawiane raz i potem tylko się do niego odwołujemy, nie ma żadnego kolejnego połączenia. Chyba, że czegoś nie zrozumiałem...

Pozdrawiam.
cadavre
Tak - jak najbardziej jest jak mówisz. Ale porównuję stosowanie statyków i tworzenie nowych instancji bez singletona.
menic
Czyli tak jak sie domyślałem. Ten singleton to taki lekki pic na wode winksmiley.jpg
cadavre
Ja też nie widzę sensu w singletonie. Jeśli zdefiniuję sobie:
  1. <?php
  2. $db = new db();
  3. ?>
to dalej w kodzie będę cały czas używał $db a nie tworzył nowe jej instancje, bo po co? A gdy chcę uzyskać dostęp do klasy db w innych klasach to używam już tylko extends'a i komunikuję się z klasą db poprzez Scope Resolution Operator. Naturalnie połączenie z bazą danych uzyskuję na początku głównego pliku, który ładuje wszystkie moduły etc.

EDIT: Gdy potrzebuję przetestować jakąś klasę, która używa np. połączenia z bazą to używam do tego platformy testowej napisanej przeze mnie samego.
y3ti
Zgadza się, ale Twój obiekt $db jest zmienną globalną - a przed tym ma właśnie chronić singleton. Powiedzmy, że masz taką sytuację:

  1. <?php
  2. (...)
  3. $db = new DB();
  4.  
  5. class Foo {
  6.  function bar() {
  7.  // $db nie istnieje, musimy korzystać z global
  8.  }
  9. }
  10. ?>


W bar() $db nie istnieje. Musisz wywołać jeszcze raz $db = new DB() - co wiąże się z tym, że będziesz miał dwa takie same obiekty. Możesz również korzystać ze zmiennej globalnej - a fuj nie ładne.

Możesz skorzystać z singletona, który gwarantuje jedną instancje i tylko jedną. Dla przykładu masz klasę Preferences, która przechowuje ustawienia dla całego serwisu. Jeśli klasa X zmieni coś w Preferences to klasa Z musi odczytać tą zmianę (nie mogą być dwa różne obiekty Preferences - może być tylko jeden).

Jednak nadal nie rozumiem, czym się różni funkcjonalnością klasa statyczna od singletona.

Klasa statyczna również:
- nie zezwala na dwie różne wersje objektów
- można przechowywać składowe (np. identyfikator połączenia z bazą danych) - private static
- zachowana jest hermetyzacja

Poza tym korzystanie ze statycznej klasy jest szybsze (nie trzeba pobierać instancji) tj.

  1. <?php
  2. // zamiast 
  3. $db = DB:getInstance();
  4. $db->query(...);
  5.  
  6. // Możemy napisać po prostu
  7. DB:query(...);
  8. ?>
cadavre
O właśnie y3ti zauważyłem błąd w swoim poście. Klasy, które używają db tą właśnie klasę extendują i wszystkie procedury wykonywane są poprzez Scope Resolution Operator (:smile.gif. Moje frameworki działają na zasadzie identycznej (o ile się nie mylę) z Zendową - istnieje główna klasa, która jest extendowana przez wszystkie inne - dzięki temu mam dostęp do wszystkich klas.

EDIT:
  1. <?php
  2. (...)
  3. $db = new DB();
  4.  
  5. class Foo {
  6.  function bar() {
  7.  // $db nie istnieje, musimy korzystać z global
  8.  }
  9. }
  10. ?>
Tak samo jeśli pierwszą linijkę zastąpisz przez:
  1. <?php
  2. $db = DB::getInstance();
  3. ?>
bo jak w kolejnej ładowanej klasie uzyskasz dostęp do $db? Również musisz globalować.
y3ti
Masz rację, ale dzięki singleton możesz stworzyć tylko i wyłącznie jedną instancję obiektu.

Co do różnic pomiędzy singleton a statyczną klasą tak mi coś jeszcze w głowie zaświtało. Gdy taka klasa DB jest finalna to w sumie nie robi różnicy (dla mnie) czy korzystam ze statycznej wersji DB, czy z singletonu.
Jednak co się stanie jeśli będziemy dziedziczyć z DB?
cadavre
Cytat
php 5 introduces the final keyword, which prevents child classes from overriding a method by prefixing the definition with final. If the class itself is being defined final then it cannot be extended.


Finalnej klasy nie można extendować - wiadomo. A gdy dziedziczysz z db to po prostu nie możesz zastąpić metod z klasy finalnej metodami, utworzonymi w klasie dziedziczącej. Różnicy pomiędzy singletonem a statykiem w tym momencie nie widzę.

EDIT: U mnie db nie jest finalną, a finalnymi są te, które extendują db i nie są przez nic extendowane. Final nie służy do wersjonowania, a raczej do tego by zabezpieczyć klasę przez jej extendowaniem, a tym samym możliwą podmianą metod. Tzn. ja tak stosuję final. tongue.gif
y3ti
brrr zagalopowałem się z tymi klasami finalnymi winksmiley.jpg

Chodziło mi, że różnica jest chyba jest jak będziemy dziedziczyć z klasy statycznej albo singleton - obie klasy oczywiście niefinalne smile.gif Chodzi o sposób odwoływania się np. do składowych odziedziczonych po rodzicu.

W sumie różnicy nie widzę w funkcjonalności klasy statycznej a singleton. Mimo to wzorzec singleton wymyśliła mądrzejsza od nas osoba, która na 100% była świadoma istnienia klas statycznych i dla tego singleton musi być lepszym rozwiązaniem - tylko dlaczego? winksmiley.jpg
cadavre
Swoją drogą final'e stosuje się raczej w końcowych fazach projektów, rzadko kiedy w dev-time. Oczywiście jeśli dany moduł (np. klasa właśnie) jest gotowy przed innymi to jak najbardziej można zastanowić się nad finalem. Z praktyki jednak wiem, że nawet gotowe moduły często ulegają zmianie.

Big thx 4 PHP5ZP. biggrin.gif
Denver
Wydaje mi się, że siłą rzeczy klasa zawierająca same właściwości i metody statyczne będzie po prostu wolniejsza od jej odpowiednika opartego na wzorcu singleton. Radzę porobić testy wydajnościowe, ale intuicja mi mówi, że Singleton po prostu będzie szybszy.
y3ti
Pokombinowałem troszkę i...

1. Klasa statyczna nie może posiadać konstruktora (destruktor działa), więc nie jesteśmy w stanie kontrolować procesu tworzenia instancji klasy

2. Korzystając z klas statycznych możemy zapomnieć o polimorfizmie. Musimy przekazać instancje klasy do metody - w klasie statycznej nie tworzymy instancji

Klasy statyczne dobrze się sprawdzają jako fabryki.
Jarod
Cytat(y3ti @ 26.12.2006, 12:34:23 ) *
Pokombinowałem troszkę i...

1. Klasa statyczna nie może posiadać konstruktora (destruktor działa), więc nie jesteśmy w stanie kontrolować procesu tworzenia instancji klasy


A po co chcesz tworzyć instancję klasy statycznej? blink.gif
hwao
y3ti tłumaczy wam absurd jaki przedstawiliście, mianowicie:

"singleton to taki nie potrzebny bajer, lepsza klasa statyczna".

Jak ktoś nie widzi różnicy, potrzeby to niech nie korzysta z singletona. Jest to jeden z najpopularniejszych wzorców i najczęściej stosowany.
menic
Skoro nie widze róznicy to niech ktoś przedstawi róznice w praktyce, a nie tylko w teorii.
cadavre
I jeśli
Cytat
Jest to jeden z najpopularniejszych wzorców i najczęściej stosowany.
to dlaczego? biggrin.gif
y3ti
Załóżmy, że mamy klasę DB służącą do obsługi bazy danych oraz klasę DB_Mysql służącą do obsługi bazy MySQL. W rzeczywistym systemie klasa DB była by pewnie abstrakcyjna, ale załóżmy, że nasza klasa ma jakąś funkcjonalność winksmiley.jpg

Mamy też funkcję, która wykonuje jakąś operacje na na bazie danych. Do tego potrzebny jest jej obiekt klasy DB. Funkcja nie wie z jakiego silnika będziemy korzystać, więc po prostu prosi o obiekt typu DB.

Wersja statyczna:

  1. <?php
  2. class DB {
  3. public static function hello() {
  4. print 'Funkcja query klasy DB'; 
  5. }
  6. }
  7.  
  8. class DB_Mysql extends DB {
  9. public static function hello() {
  10. print 'Funkcja query klasy DB_Mysql';
  11. }
  12. }
  13.  
  14. //
  15. // Zalozmy, ze mamy funkcje, ktora wykonuje jakies zapytanie 
  16. // do bazy danych. Funkcja nie wie z jakiego silnika bazy danych
  17. // korzystamy, wiec korzystamy z dobrodziejstw polimorfizmu
  18. //
  19. function foo(DB $objDB) {
  20. $objDB->hello();
  21. }
  22.  
  23. //
  24. // Nie mozemy zrobic czegos takiego
  25. //
  26. foo(DB_Mysql);
  27. ?>


php wywali nam błąd, ponieważ funkcja foo() oczekuje instancji obiektu DB.

A teraz Singleton:

  1. <?php
  2. class DB {
  3. private static $instance;
  4.  
  5. private function __construct() {}
  6.  
  7. public static function getInstance() {
  8. if(empty(self::$instance)) 
  9. self::$instance = new DB();
  10.  
  11. return self::$instance;
  12. }
  13.  
  14. public static function hello() {
  15. print 'Funkcja query klasy DB'; 
  16. }
  17. }
  18.  
  19.  
  20. class DB_Mysql extends DB {
  21. private static $instance;
  22.  
  23. private function __construct() {}
  24.  
  25. public static function getInstance() {
  26. if(empty(self::$instance)) 
  27. self::$instance = new DB_Mysql();
  28.  
  29. return self::$instance;
  30. }
  31.  
  32. public static function hello() {
  33. print 'Funkcja query klasy DB_Mysql'; 
  34. }
  35. }
  36.  
  37. //
  38. // Zalozmy, ze mamy funkcje, ktora wykonuje jakies zapytanie 
  39. // do bazy danych. Funkcja nie wie z jakiego silnika bazy danych
  40. // korzystamy, wiec korzystamy z dobrodziejstw polimorfizmu
  41. //
  42. function foo(DB $objDB) {
  43. $objDB->hello();
  44. }
  45.  
  46. $db = DB::getInstance();
  47. foo($db); // wyswietli: Funkcja query klasy DB 
  48.  
  49. $db2 = DB_Mysql::getInstance();
  50. foo($db2); // wyswietli: Funkcja query klasy DB_Mysql
  51. ?>


Tak na szybko, tylko taki przykład przyszedł mi do głowy winksmiley.jpg
menic
Ok to takie pytanie. Do czego sie najbardziej przydają klasy statyczne?
y3ti
Np. jako wzorzec fabryki

  1. <?php
  2.  $object = DB::factory('MySQL');
  3. ?>
dr_bonzo
'Klasa statyczna' od Singletona rozni sie tylko (i az) tym ze nie jest obiektem, i inaczej sie zapisuje wywolania metod.
Athlan
Na podstawie wzorca singletorn można stwożyć klasę rejestru, czyli zbiór konkretnych instancji klasy. Zazwyczaj złużą temu dwie metody:
Register() - rejestruje instancje klas
Registry() - wywołuje instancje klas z biblioteki singletonów

Singletony przechowywane są jako tablica w prywatnym statycznym atrybucie, jakoże w.w metody również są statyczne.

Trochę kodu wyciągniętego z mojego FW...

  1. <?php
  2.  
  3. final class Vframe
  4. {
  5. // ...
  6.  
  7. /**
  8.  * Object registry provides storage for shared objects
  9.  * @var array
  10.  */
  11. private static $_aRegistry = array();
  12.  
  13. // ...
  14.  
  15. /**
  16.  * Register a object as specified name
  17.  * If $sRegistryName is NULL, than name of registry will be name of declared cla
    ss
  18.  * 
  19.  * @see: registry()
  20.  * @throws: VframeException
  21.  * @param $oInstance  object : object witch will be push into registry array
  22.  * @param $sRegistryName string : name as registry object
  23.  * @access: public
  24.  * @return: void
  25.  */
  26. public static function register($oInstance, $sRegistryName = NULL)
  27. {
  28. if(!is_object($oInstance))
  29. throw new VframeException('Only objects may be stored in the registry!');
  30.  
  31. if($sRegistryName === NULL)
  32. $sRegistryName = get_class($oInstance);
  33.  
  34. if(!is_string($sRegistryName))
  35. throw new VframeException('Second parametr $sRegistryName must be a string!');
  36.  
  37. if(isset(self::$_aRegistry[$sRegistryName]))
  38. throw new VframeException('Registry "'.$sRegistryName.'" arleady exists! Did you mean to call registry()?');
  39.  
  40. self::$_aRegistry[$sRegistryName] = $oInstance;
  41. }
  42.  
  43. /**
  44.  * Get a single instance (singleton) from registry
  45.  * If $sInstanceName is NULL, than will be returned
  46.  * array of registereg objects
  47.  * 
  48.  * @see: register()
  49.  * @throws: VframeException
  50.  * @param $sInstanceName string : key of registry, witch will be return
  51.  * @access: public
  52.  * @return: object
  53.  */
  54. public static function registry($sInstanceName = NULL)
  55. {
  56. if($sInstanceName === NULL)
  57. {
  58. $sRegistryMap = array();
  59.  
  60. foreach(self::$_aRegistry as $sRegistryName => $oRegistryObject)
  61. {
  62. $sRegistryMap[$sRegistryName] = get_class($oRegistryObject);
  63. }
  64.  
  65. return $sRegistryMap;
  66. }
  67.  
  68. if(!is_string($sInstanceName))
  69. throw new VframeException('Param $sInstanceName must be a string!');
  70.  
  71. if(isset(self::$_aRegistry[$sRegistryName]))
  72. throw new VframeException('No object named as "$sInstanceName", use register() to insert it!');
  73.  
  74. return self::$_aRegistry[$sInstanceName];
  75. }
  76.  
  77. // ...
  78. }
  79.  
  80. ?>
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.