menic
23.12.2006, 22:55:17
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
24.12.2006, 00:08:40
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
24.12.2006, 00:15:31
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).
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
25.12.2006, 14:02:51
Z teoretycznego punktu widzenia rozumiem. Gorzej z praktycznego
hwao
25.12.2006, 14:40:00
Hm patrz

<?php
function NewSpace() {
// Jakas nowa przestrzen, tu nie ma zmiennej $Object, z tamtąd
// łapiemy dziada
$Object = Counter::get();
echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL
;
$Object->run();
echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL
;
}
class Counter {
private $i = -1;
private function __construct() {
$this->i = 0;
}
private function __clone() {}
private static $Instance = null; public static function get
() { return is_null( self::$Instance ) ?
self::$Instance = new Counter
() : self::$Instance; }
public function run() {
$this->i++;
}
public function getStatus() {
return $this->i;
}
}
// Nie darady
//$Object = new Counter();
// O, tak lepiej :)
$Object = Counter::get();
echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL
;
$Object->run();
echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL
;
NewSpace();
echo 'Włączony: '.$Object->getStatus().' razy.'.PHP_EOL
;
?>
Ciagle operujesz na tym samym obiekcie, nie możesz "powielić".
<?php
// Nie darady, prywatny konstrukor
$Object = new Counter();
$Object = Counter::get();
// Sklonowac dziada tez sie nie da :)
$CloneObject = clone $Object;
?>
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
25.12.2006, 14:53:58
Rozumiem

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
25.12.2006, 16:00:54
Odpowiedź jest niezwykle prosta - od potrzeb : )
Pozdrawiam.
cadavre
25.12.2006, 17:57:15
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
25.12.2006, 18:04:19
@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
25.12.2006, 18:38:32
Tak - jak najbardziej jest jak mówisz. Ale porównuję stosowanie statyków i tworzenie nowych instancji bez singletona.
menic
25.12.2006, 20:08:54
Czyli tak jak sie domyślałem. Ten singleton to taki lekki
pic na wode
cadavre
25.12.2006, 20:35:45
Ja też nie widzę sensu w singletonie. Jeśli zdefiniuję sobie:
<?php
$db = new db();
?>
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
25.12.2006, 20:52:44
Zgadza się, ale Twój obiekt $db jest zmienną globalną - a przed tym ma właśnie chronić singleton. Powiedzmy, że masz taką sytuację:
<?php
(...)
$db = new DB();
class Foo {
function bar() {
// $db nie istnieje, musimy korzystać z global
}
}
?>
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.
<?php
// zamiast
$db = DB:getInstance();
$db->query(...);
// Możemy napisać po prostu
DB:query(...);
?>
cadavre
25.12.2006, 20:56:44
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 (:

. 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:
<?php
(...)
$db = new DB();
class Foo {
function bar() {
// $db nie istnieje, musimy korzystać z global
}
}
?>
Tak samo jeśli pierwszą linijkę zastąpisz przez:
<?php
$db = DB::getInstance();
?>
bo jak w kolejnej ładowanej klasie uzyskasz dostęp do $db? Również musisz globalować.
y3ti
25.12.2006, 21:16:42
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
25.12.2006, 23:00:28
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.
y3ti
25.12.2006, 23:20:34
brrr zagalopowałem się z tymi klasami finalnymi

Chodziło mi, że różnica jest chyba jest jak będziemy dziedziczyć z klasy statycznej albo singleton - obie klasy oczywiście niefinalne

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?
cadavre
26.12.2006, 00:25:38
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.
Denver
26.12.2006, 10:28:10
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
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
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
26.12.2006, 13:45:06
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?
hwao
26.12.2006, 15:00:21
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
26.12.2006, 15:32:18
Skoro nie widze róznicy to niech ktoś przedstawi róznice w praktyce, a nie tylko w teorii.
cadavre
26.12.2006, 17:50:39
I jeśli
Cytat
Jest to jeden z najpopularniejszych wzorców i najczęściej stosowany.
to dlaczego?
y3ti
26.12.2006, 17:53:03
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ść

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:
<?php
class DB {
public static function hello
() { print 'Funkcja query klasy DB'; }
}
class DB_Mysql extends DB {
public static function hello
() { print 'Funkcja query klasy DB_Mysql'; }
}
//
// Zalozmy, ze mamy funkcje, ktora wykonuje jakies zapytanie
// do bazy danych. Funkcja nie wie z jakiego silnika bazy danych
// korzystamy, wiec korzystamy z dobrodziejstw polimorfizmu
//
function foo(DB $objDB) {
$objDB->hello();
}
//
// Nie mozemy zrobic czegos takiego
//
foo(DB_Mysql);
?>
php wywali nam błąd, ponieważ funkcja foo() oczekuje instancji obiektu DB.
A teraz Singleton:
<?php
class DB {
private function __construct() {}
public static function getInstance
() { if(empty(self::$instance)) self::$instance = new DB();
return self::$instance;
}
public static function hello
() { print 'Funkcja query klasy DB'; }
}
class DB_Mysql extends DB {
private function __construct() {}
public static function getInstance
() { if(empty(self::$instance)) self::$instance = new DB_Mysql();
return self::$instance;
}
public static function hello
() { print 'Funkcja query klasy DB_Mysql'; }
}
//
// Zalozmy, ze mamy funkcje, ktora wykonuje jakies zapytanie
// do bazy danych. Funkcja nie wie z jakiego silnika bazy danych
// korzystamy, wiec korzystamy z dobrodziejstw polimorfizmu
//
function foo(DB $objDB) {
$objDB->hello();
}
$db = DB::getInstance();
foo($db); // wyswietli: Funkcja query klasy DB
$db2 = DB_Mysql::getInstance();
foo($db2); // wyswietli: Funkcja query klasy DB_Mysql
?>
Tak na szybko, tylko taki przykład przyszedł mi do głowy
menic
29.12.2006, 12:00:20
Ok to takie pytanie. Do czego sie najbardziej przydają klasy statyczne?
y3ti
29.12.2006, 12:23:06
Np. jako wzorzec fabryki
<?php
$object = DB::factory('MySQL');
?>
hwao
30.12.2006, 12:55:24
dr_bonzo
30.12.2006, 14:18:17
'Klasa statyczna' od Singletona rozni sie tylko (i az) tym ze nie jest obiektem, i inaczej sie zapisuje wywolania metod.
Athlan
17.01.2007, 07:49:10
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...
<?php
final class Vframe
{
// ...
/**
* Object registry provides storage for shared objects
* @var array
*/
// ...
/**
* Register a object as specified name
* If $sRegistryName is NULL, than name of registry will be name of declared cla
ss
*
* @see: registry()
* @throws: VframeException
* @param $oInstance object : object witch will be push into registry array
* @param $sRegistryName string : name as registry object
* @access: public
* @return: void
*/
public static function register
($oInstance, $sRegistryName = NULL) {
throw new VframeException('Only objects may be stored in the registry!');
if($sRegistryName === NULL)
$sRegistryName = get_class($oInstance);
throw new VframeException('Second parametr $sRegistryName must be a string!');
if(isset(self::$_aRegistry[$sRegistryName])) throw new VframeException('Registry "'.$sRegistryName.'" arleady exists! Did you mean to call registry()?');
self::$_aRegistry[$sRegistryName] = $oInstance;
}
/**
* Get a single instance (singleton) from registry
* If $sInstanceName is NULL, than will be returned
* array of registereg objects
*
* @see: register()
* @throws: VframeException
* @param $sInstanceName string : key of registry, witch will be return
* @access: public
* @return: object
*/
public static function registry
($sInstanceName = NULL) {
if($sInstanceName === NULL)
{
foreach(self::$_aRegistry as $sRegistryName => $oRegistryObject)
{
$sRegistryMap[$sRegistryName] = get_class($oRegistryObject);
}
return $sRegistryMap;
}
throw new VframeException('Param $sInstanceName must be a string!');
if(isset(self::$_aRegistry[$sRegistryName])) throw new VframeException('No object named as "$sInstanceName", use register() to insert it!');
return self::$_aRegistry[$sInstanceName];
}
// ...
}
?>
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.