Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Maarre, als je toch een factory bouwt voor je singleton (wat an sich al vreemd is), waarom hou je dan niet gewoon je singleton instances bij in de factory zelf?
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
vb:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| class singletonfactory { public function getInstanceOf( $classname ) { $instance = $classname::getInstance(); //singleton class instantie ophalen return $instance; } } $factory = new singletonfactory; $mijnsingleton = $factory->getInstanceOf( 'eensingletonclass' ); |
normale classen kan ik gewoon instantieren op deze manier, de variabele classen werken namelijk wel i.c.m. het new keyword, echter dus niet met statische methods.
[ Voor 0% gewijzigd door tombo_inc op 16-11-2006 23:00 . Reden: typo ]
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
http://nl3.php.net/manual/en/function.call-user-func.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class singletonfactory { private $instanceList; public function __construct() { $this->instanceList = array(); } public function getInstanceOf( $classname ) { if(!isset($this->instanceList[$classname])) $this->instanceList[$classname] = new $classname; return $this->instanceList[$classname]; } } |
Zoiets.
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
maar never mind
de call_user_func methode is mijn oplossing. ik kan daarmee namelijk een static method aanroepen op een variabele class, en dat is wat ik nodig had.
bedankt in ieder geval voor het meedenken
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Je bent gewoon hier patterns aan het overusen.
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
In jouw geval geef je al prijs om welke klasse het gaat en dat doet al in zekere zin afbreuk aan abstractie. Daarbij hebben de klassen welliswaar een interface Singleton met getInstance methode gemeen, de vraag is echter wat het praktisch nut van dit alleen is in z'n totaliteit.
Moraal van het verhaal: gebruik patterns alleen als je zo ook begrijpt.
[ Voor 4% gewijzigd door prototype op 17-11-2006 05:17 ]
Wat is hier precies het nut van? Als je toch aan je factory de class name moet specificeren en je factory roept alleen maar getInstance() op die class aan dan kan je dat toch net zo goed direct doen? Ik zie niet hoe dit jouw probleem van het alleen instantieren van singletons als je ze nodig hebt oplost. Ik zie de meerwaarde van de factory niet.tombo_inc schreef op donderdag 16 november 2006 @ 23:00:
dit is niet helemaal wat ik wil. ik heb namelijk een aantal singleton classen. deze classen zijn soms wel nodig en soms niet, ik wil ze dus alleen instantieren als ze nodig zijn. daarom maak ik een factory die op mijn commando de juiste class moet laden. als ik standaard alle classen instanties bij ga houden in mijn factory dan is het nut van dit principe een beetje weg lijkt mij.
vb:
PHP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class singletonfactory { public function getInstanceOf( $classname ) { $instance = $classname::getInstance(); //singleton class instantie ophalen return $instance; } } $factory = new singletonfactory; $mijnsingleton = $factory->getInstanceOf( 'eensingletonclass' );
normale classen kan ik gewoon instantieren op deze manier, de variabele classen werken namelijk wel i.c.m. het new keyword, echter dus niet met statische methods.
Als je dat probleem wil oplossen kan je gewoon een singleton met lazy instantiation maken. Waarbij de static getInstance() methode dus gewoon checkt of de static instance null is of niet, zo ja dan maakt hij een nieuwe instantie en returnt die. Zo nee dan returnt hij gewoon de bestaande instantie.
Roep je getInstance op je singleton nooit aan dan wordt hij ook nooit geinstantieerd.
Verwijderd
Verwijderd
Ik begrijp je vraag niet. Wat je bedoel je hiermee?Verwijderd schreef op vrijdag 17 november 2006 @ 10:35:
Ik neem maar even aan dat het gaat om een webapplicatie. Wat is dan het nut van lazy initialisation in een langlopend process?
Verwijderd
Dat het vrij nutteloos is om lazy te initialiseren in een langlopend process. De kans is immers vrij groot dat elke instantie uiteindelijk wordt geladen. Dus het aanmaken van een instantie in de getInstance method is vrij nutteloos. Maak deze gewoon aan bij het inladen van de applicatie (hoewel ik me wel realiseer dat php hierin hoogstwaarschijnlijk vrij beperkt is).Verwijderd schreef op vrijdag 17 november 2006 @ 10:43:
Ik begrijp je vraag niet. Wat je bedoel je hiermee?
Verwijderd
Dus ik zie eigenlijk niet zo in waarom je het niet zou gebruiken en daarom vroeg ik me dat dus af
Verwijderd
Omdat bij lazy-initialization ook threading een duidelijk rol gaat spelen. Twee threads die gelijktiijdig voor het eerst de instantie gaan opvragen kunnen er voor zorgen dat er meer dan 1 instantie van de singleton class bestaan. Je zal dus moeten synchroniseren. En dat maakt het nodeloos complex...Verwijderd schreef op vrijdag 17 november 2006 @ 11:04:
Dus ik zie eigenlijk niet zo in waarom je het niet zou gebruiken en daarom vroeg ik me dat dus af.
Ze handelen een request af en dan is het weer finito. Als je dan wat cpu cycles en resources kan besparen door niet nodeloos allerlei classes te gaan instantieren die je executie pad mogelijk helemaal niet gaat gebruiken waarom zou je dat dan nalaten?. Tuurlijk hangt het uiteindelijk nut grotendeels af van wat die singletons dan allemaal doen op het moment dat er een instantie aangemaakt wordt. Maar als je er classes tussen hebt zitten die met een database aan de haal gaan of met file I/O (config files parsen, ofzo) dan kan je toch wat tijd besparen.
En het is niet zo dat een lazy singleton maken veel extra moeite kost (ik zou zelfs zeggen geen extra moeite) en die extra null check in de getInstance() zal je ook niet in de performance terug zien.
Ik zie niet direct een overduidelijk reden waarom je dit zou vermijden.
Verwijderd
Dat is me bekend. Maar voor zover ik weet is het niet mogelijk binnen PHP om threading te gebruiken dus dit probleem doet zich volgens mij niet voor. En dus is het ook niet nodeloos complexVerwijderd schreef op vrijdag 17 november 2006 @ 11:11:
[...]
Omdat bij lazy-initialization ook threading een duidelijk rol gaat spelen. Twee threads die gelijktiijdig voor het eerst de instantie gaan opvragen kunnen er voor zorgen dat er meer dan 1 instantie van de singleton class bestaan. Je zal dus moeten synchroniseren. En dat maakt het nodeloos complex...
Dat is een goed punt, ware het niet dat PHP geen ingebouwde mogelijkheden heeft voor multi-threading. Dus alles is in PHP gewoon per definitie thread-safe.Verwijderd schreef op vrijdag 17 november 2006 @ 11:11:
[...]
Omdat bij lazy-initialization ook threading een duidelijk rol gaat spelen. Twee threads die gelijktiijdig voor het eerst de instantie gaan opvragen kunnen er voor zorgen dat er meer dan 1 instantie van de singleton class bestaan. Je zal dus moeten synchroniseren. En dat maakt het nodeloos complex...
Verwijderd
Dit zou voor mij inderdaad de reden zijn om standaard lazy initialization te gebruiken. Mocht PHP ooit (CAIRATH schreef op vrijdag 17 november 2006 @ 11:13:
[...]
Maar als je er classes tussen hebt zitten die met een database aan de haal gaan of met file I/O (config files parsen, ofzo) dan kan je toch wat tijd besparen.
[...]
Verwijderd
Hoezo niet? Ik instantieer twee modules die elk DB aanroepen moeten doen. Dan is het best fijn om maar 1 instantie van de DB module af te dwingen.Verwijderd schreef op vrijdag 17 november 2006 @ 11:23:
Jullie hebben gelijk, soms verbeeld ik me wel eens dat php daadwerkelijk een fatsoenlijke taal isDan heeft singleton (wat al een beperkt bestaansrecht heeft) helemaal geen bestaansrecht binnen php.
Wat de TS gewoon fout doet is een factory pattern en een singleton pattern te combineren, want als je een factory gebruikt dan hoef je die singleton implementatie niet te maken, dat laat je de factory doen.
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
1
2
3
4
5
6
7
8
9
10
11
12
13
| <?php class singletonfactory { public function getInstanceOf( $classname ) { $instance = call_user_func($classname, 'getInstance'); //singleton class instantie ophalen return $instance; } } ?> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <?php class Singleton { private static $instance; public static function getInstance() { if ( is_null(self::$instance) ) { self::$instance = new Singleton(); } return self::$instance; } } ?> |
Zo doet ie toch precies wat je wilt?
Zo'n SingletonFactory lijkt me vrij nutteloos eerlijk gezegd. Misschien dat je er goede redenen voor hebt, daar ben ik wel benieuwd naar dan.
Verwijderd
Wat Mark volgens mij wil aangeven is het volgende:Grijze Vos schreef op vrijdag 17 november 2006 @ 11:46:
Hoezo niet? Ik instantieer twee modules die elk DB aanroepen moeten doen. Dan is het best fijn om maar 1 instantie van de DB module af te dwingen.
Daarom is het gebruik van een singelton om een database connectie te pakken te krijgen misschien ook wel niet zo goed en zou je dat op een andere manier willen doen. Verder is deze oplossing minder goed onderhoudbaar want stel dat ik nu meerdere database connecties wil hebben, wat dan? Het singelton pattern zegt dat er maar één instantie kan bestaan maar bij een database connectie is het toch mogelijk om meerdere connecties te hebben... dus kun je je afvragen of het gebruik van het singelton pattern hier dan goed is.Bron: Wikipedia - Singleton pattern
In software engineering, the singleton design pattern is used to restrict instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. Sometimes it is generalized to systems that operate more efficiently when only one or a few objects exist. It is also considered an anti-pattern since it is often used as a euphemism for global variable. Before designing a class as a singleton, it is wise to consider whether it would be enough to design a normal class and just use one object.
Het gebruik van het singelton pattern is dus alleen maar bruikbaar bij objecten waarvan maar één instantie kan bestaan. Gaat je applicatie uit van maximaal één database connectie dan valt er iets voor te zeggen maar persoonlijk ben ik er in dat geval geen voorstander van.
Dat is wat ik gedaan heb in mijn stukje code een paar posts omhoog.Verwijderd schreef op vrijdag 17 november 2006 @ 11:50:
Omdat je simpelweg 1 instantie door kan geven. Heb je heel singleton niet voor nodig.
Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info
1
2
3
4
5
6
7
8
9
10
11
12
13
| class SingletonNode { private static $instances; protected static $errorNode; private function __construct() { } public static function getInstanceOf($class) { if(!isset(self::$instances[$class])) { self::$instances[$class] = new $class; } return self::$instances[$class]; } |
Verwijderd
Ja, volgens mij wel.Verwijderd schreef op vrijdag 17 november 2006 @ 12:42:
even voor mijn begrip: is een php static var een thread/request-scope static?
Dat is toch min of meer een implementatie van het registry pattern.tech-no-logical schreef op vrijdag 17 november 2006 @ 12:24:
stukje zoals ik het gebruik, denk dat dit is wat de ts zoekt ?
PHP:
1 2 3 4 5 6 7 8 9 10 11 12 13 class SingletonNode { private static $instances; protected static $errorNode; private function __construct() { } public static function getInstanceOf($class) { if(!isset(self::$instances[$class])) { self::$instances[$class] = new $class; } return self::$instances[$class]; }
klopt. het groeide bij mij echter uit een implementatie die begon als singleton, en uiteindelijk werd tot een baseclass (de singleton) en een set extended classes die dezelfde interface implementeren. de baseclass is daarmee een soort van gepromoveerd tot de registry.Brakkie schreef op vrijdag 17 november 2006 @ 13:16:
Dat is toch min of meer een implementatie van het registry pattern.
Verwijderd
Nogmaals, wanneer een object het singleton pattern implementeert is het niet mogelijk om meerdere instanties van het object te hebben (binnen welke scope dan ook). Dat kan hier dus wel... voorbeeld:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class SingletonNode { private static $instances = array(); private function __construct() { } public static function getInstanceOf($class) { if(!isset(self::$instances[$class])) { self::$instances[$class] = new $class; } return self::$instances[$class]; } } class Registry { } $reg1 = SingletonNode::getInstanceOf('Registry'); $reg2 = new Registry(); |
Edit: ok, het is je dus wel duidelijk
Voor je begrip, de enige scopes die php kent zijn request, class en function. Session, page en aplpication scope heeft php niet en zelfs een foreach lekt zijn looping parameters naar global scope.Verwijderd schreef op vrijdag 17 november 2006 @ 12:42:
even voor mijn begrip: is een php static var een thread/request-scope static?
Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'
ter aanvulling : de extended classes hebben (natuurlijk) een protected constructor... misschien dat 't alles hierdoor voor de ts misschien wat minder toepasbaar is, en ts beter een volledig geimplementeerd registry-pattern kan gebruiken...
ten eerste is mijn probleem al opgelost m.b.v. de call_user_func functie. namelijk op de manier die maartenba ook al liet zien.Michali schreef op vrijdag 17 november 2006 @ 12:03:
Zo'n SingletonFactory lijkt me vrij nutteloos eerlijk gezegd. Misschien dat je er goede redenen voor hebt, daar ben ik wel benieuwd naar dan.
ten tweede zal ik uitleggen waarom ik voor deze methode kies omdat dat voor de meeste van jullie niet duidelijk is. daarbij moet ik zeggen dat ik deze oplossing net wat anders gebruik dan de voorbeelden die ik gepost heb in dit topic, omdat dat het simpeler hield. hier dus mijn situatie:
ik ben bezig met een framework c.q. grote code bibliotheek. nou wil ik een interface maken naar die code bibliotheek en daarmee wil ik mijn onderliggende library structuur abstraheren voor het bovenliggende systeem. ik heb dus een heleboel componenten/classes die ik in mijn te bouwen applicatie wil kunnen gebruiken. de interface van mijn framework implementeerd nu functionaliteit om instanties van die componenten/classes te krijgen. mijn interface is hier dus een factory die classe instanties terug geeft.
vb:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class mainfactory { public static function getInstanceOf( $componentname ) { //map componentnaam naar classenaam ($classname) //laad classfile //check singleton of niet if( $is_singleton ) { $instance = call_user_func( array( $classname, 'getInstance' ) ); } else { $instance = new $classname; } return $instance; } } //vb $sesshandler = mainfactory::getInstanceOf( 'sessionhandler' ); //haal een sessionhandler object op |
omdat ik in mijn framework zowel singletons als niet singletons gebruik moet mijn factory zowel singletons als niet singletons terug kunnen geven. omdat ik tevens de gebruiker niet lastig wil vallen met classe namen gebruik ik eenvoudige en duidelijke componentnamen. de factory zorgt er vervolgens voor dat de juiste classes geladen worden. wat ik dus niet wil is om standaard al mijn objecten te laden, want dat genereerd een hele berg overhead die helemaal niet nodig is, aangezien vaak lang niet alle componenten gebruikt worden in de applicatie.
ik hoop dat ik met dit voorbeeld duidelijk heb kunnen maken waarom ik voor deze aanpak gekozen heb. en ik hoop dat jullie het nu met mij eens zijn. commentaar is altijd welkom want ik wil uiteraard horen of het beter kan etc.
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| <?php $componentFactoryClassMap = array( 'component1' => 'Component1Factory', 'component2' => 'component_2_factory', 'component3' => 'Comp3Fact' ); class Component1Class {} // Singleton class component_2_class {} // geen Singleton class component3 {} // constructor met paramters interface ComponentFactory { public function create(); } class Component1Factory implements ComponentFactory { public function create() { return Component1Class::getInstance(); } } class component_2_factory implements ComponentFactory { public function create() { return new component_2_class(); } } class Comp3Fact implements ComponentFactory { public function create() { return new component3($param1, $param2); } } class ComponentManager { public static function getComponent($name) { if ( isset($GLOBALS['componentFactoryClassMap'][$name]) ) { $factoryClass = $GLOBALS['componentFactoryClassMap'][$name]; $factory = new $factoryClass; return $factory->create(); } return null; // of een exception gooien, wat je wilt } } ?> |
Je wilt duidelijk gebruik maken van polymorphisch gedrag, dan moet je er niet om heen gaan draaien, maar het ook werkelijk zo toepassen. Deze oplossing geeft je veel meer mogelijkheden. Het creëren van het object kun je nu regelen zoals je wilt.
Mocht je het nodig vinden (afhankelijk van hoe vaak je een component ophaalt), kun je de factory objecten ook cachen. Als dit niet nodig is, dan kun je het zo laten. Een objectje extra instantieren levert niet zo veel problemen op.
je hebt 3 verschillende typen classen (maar dat mogen er natuurlijk meer zijn). voor ieder type class maak je een eigen factory class aan die afgestemt is om een object van 1 type te instantieren.
jij hebt dus 3 factory classen omdat je 3 verschillende soorten classen hebt.
vervolgens heb je je parent factory die dient als een soort wrapper voor de losse type factories, en dus de juiste factories aan roept.
klopt mijn gedachtegang?
wat ik me dan alleen nog afvraag bij type 3, met die parameters is hoe je die parameters meegeeft. want dat kan ik nog niet helemaal volgen.
en tot slot. deze aanpak die je me hier laat zien, wordt die veel gebruikt? het kost je immers wel een hoop extra classen, dit wordt dus niet als nadeel ervaren?
Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition
Je redenatie klopt verder wel. Je hebt verschillende classes, die elk een eigen manier voor het verkrijgen van een instantie hebben, maar je wilt dit wegwerken voor de client. Dan maakt je dus een interface die het gewenste gedrag uitvoert (in dit geval een instantie maken of verkrijgen), en die laat je voor de verschillende classes implementeren.
Dit kan niet in het component zelf, omdat die al voor andere doelen wordt gebruikt. Hiervoor maak je gewoon een aparte class. Overigens kun je veel voorkomende implementaties gewoon generaliseren. Scheelt je ook weer werk.
Of deze aanpak veel wordt gebruikt? Ik heb het nog nooit zo toegepast, maar ze zeggen ook wel dat ieder gebruik van een pattern meestal uniek is, als de redenen voor het gebruik maar goed zijn. Verder denk ik dat het zo niet heel veel wordt toegepast, omdat je iets probeert dat (naar mijn idee) sowieso niet heel gebruikelijk is.