Een Factory als Helper Class (statisch)
Ik ben me momenteel aan het verdiepen in de verschillende Design Patterns (voornamelijk van GoF) en heb hierover de volgende vraag:
Overal waar ik kijk worden Factories eerst geinstantieerd, waarna de methoden worden aangeroepen. Waarom zou ik een factory niet op een statische manier aanroepen (scheelt code toch?):
Zoals ik het vaak zie:
$factory = new ObjectFactory;
$object = $factory->makeNewObject();
Zoals ik het wil zien:
$object = ObjectFactory::makeNewObject();
Dus waarom gebruikt iedereen de eerste methode, terwijl de tweede methode simpeler lijkt?
Mvg,
Tim
Ik ben benieuwd waar jij je 'zoals ik het vaak zie' voorbeeld vandaan haalt? Ik kan alleen maar static voorbeelden tevoorschijn halen, de manier zoals het hoort. (een factory vind ik de enige klasse die static mag zijn)
zie het voorbeeld appel en peer (appels met peren vergelijken ;-)
Code (php)
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
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
<?php
class Appel {
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
};
$appel1 = new Appel();
$appel2 = new Appel();
$appel3 = new Appel();
$appel1->setName('golden delicious');
$appel2->setName('Jonagold');
$appel3->setName('Goudreinette');
echo $appel1->getName();
echo $appel2->getName();
echo $appel3->getName();
$object = Appel::setName('granny smit'); // geeft een foutmelding op regel 11. er is geen instantie dus $this-> mag niet.
class Peer {
public static $name = 'stoofpeer';
public static function getName() {
return self::$name;
}
};
echo Peer::getName(); // nu kan het wel
?>
class Appel {
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
};
$appel1 = new Appel();
$appel2 = new Appel();
$appel3 = new Appel();
$appel1->setName('golden delicious');
$appel2->setName('Jonagold');
$appel3->setName('Goudreinette');
echo $appel1->getName();
echo $appel2->getName();
echo $appel3->getName();
$object = Appel::setName('granny smit'); // geeft een foutmelding op regel 11. er is geen instantie dus $this-> mag niet.
class Peer {
public static $name = 'stoofpeer';
public static function getName() {
return self::$name;
}
};
echo Peer::getName(); // nu kan het wel
?>
Gewijzigd op 04/11/2012 22:02:00 door Frank Nietbelangrijk
Tevens hoort jouw code niet te kloppen, je bent de static keywords vergeten.
Gewijzigd op 04/11/2012 21:31:16 door Wouter J
Gewijzigd op 04/11/2012 22:09:23 door Frank Nietbelangrijk
Frank, ik ben bang dat je mijn vraag niet goed hebt begrepen. Om een object aan te maken zie ik twee manieren:
A) Zelf een nieuw object aanmaken met "new";
B) Een factory method ( een methode in een class die een instantie van zijn eigen class aanmaakt )
C) Een factory ( een class die verschillende vormen van een object aan kan maken )
Mijn vraag gaat over de laatste methode.
A. Keyword "new":
De welbekende methode:
Code (php)
B. Factory Method
Zie ik ook veel:
Code (php)
C. Factory
Waar mijn vraag over gaat:
Code (php)
In het laatste codeblok zie je een klasse User en een klasse UserFactory. Nu kan ik op twee manieren een user aanmaken:
C.1. Zoals ik het vaak zie:
C.2. Zoals ik het graag zou zien:
Mijn vraag
Ik hoop dat dit het duidelijker maakt. Mijn vraag is dus, waarom zie ik vaak methode C.1. in plaats van methode C.2.
Voor Wouter J, even snel gezocht: http://stackoverflow.com/questions/2083424/what-is-a-factory-design-pattern-in-php (zie het antwoord)
UserFactory is een class met enkel statische methodes. UserFactory heeft geen constructor, geen eigenschappen, geen nood aan samenwerking tussen methodes...
De toegevoegde waarde van de variabele $factory is nul.
Zolang UserFactory enkel maar blijft doen wat het nu doet, ben ik geneigd te antwoorden: "daar is niet echt een goede reden voor"
Code (php)
Dit verplaats je in OO naar een factory:
Code (php)
In dit geval is hij niet statisch, het is gewoon een methode van de Pizzaria die als Factory dient. Maar je kan in andere gevallen ook wel eens een aparte Factory klasse maken, in dit geval kunnen ze allemaal statisch. De Factory is dan namelijk helemaal geen object, maar een verzameling functies.
Kijk trouwens ook eens naar Dependency Injection en naar een Service Container, dat is de nieuwere versie van Factories.
Code (php)
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
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
<?php
class Factory {
private static $instance;
public static function create($type) {
if(!isset(self::$instance)) {
self::$instance = new Factory();
}
return self::$instance->_create($type);
}
private function __construct() {
}
private function _create($type) {
switch(type) {
case "foo":
return new Foo();
case "bar":
return new Bar();
default:
// throw some exception.
}
}
}
$bar = Factory::create("bar");
$foo = Factory::create("foo");
?>
class Factory {
private static $instance;
public static function create($type) {
if(!isset(self::$instance)) {
self::$instance = new Factory();
}
return self::$instance->_create($type);
}
private function __construct() {
}
private function _create($type) {
switch(type) {
case "foo":
return new Foo();
case "bar":
return new Bar();
default:
// throw some exception.
}
}
}
$bar = Factory::create("bar");
$foo = Factory::create("foo");
?>
Gewijzigd op 05/11/2012 14:34:08 door Gerard M
Een factory maakt verschillende objecten aan en vaak niet zichzelf. Tevens kopt je code voor geen kant en is dit alles behalve een singleton.
Wouter J op 05/11/2012 13:49:14:
Nee, een singleton is iets totaal anders Gerard. Een singleton kijk of er al een instance bestaat, zoniet dan maakt hij er eentje. Singleton is een pattern die door vele gehaat wordt, inclusief mijzelf.
Een factory maakt verschillende objecten aan en vaak niet zichzelf. Tevens kopt je code voor geen kant en is dit alles behalve een singleton.
Een factory maakt verschillende objecten aan en vaak niet zichzelf. Tevens kopt je code voor geen kant en is dit alles behalve een singleton.
In deze situatie, vind het acceptabel om middels een singleton toegang te geven tot een factory. Ik geef toe dat ik niet de klassieke "gang of four" getInstance() gebruik, maar de singleton intent c.q. use case, wordt wel degelijk bereikt. Een groot nadeel is dat polymorfisme van de Factory zelf lastiger wordt, als voordeel krijg je wel het static constructor idee, waarmee je de factory eenmalig kan "instellen". Maar goed, op het einde an de dag, kan je natuurlijk ook gewoon een factory maken die niet static is, en toch telkens een instance maken, of de instance in een "registry" stoppen.
Ik zal nu het code voorbeeld aanpassen om de singleton te fixen, en factory wat duidelijker te maken.
Code (php)
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
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
<?php
/* Interfaces voor pizzafabriek en pizza */
interface PizzaFactory
{
public function makePizza();
}
interface Pizza
{
public function getType();
}
/* Implementaties voor een Pizza Margherita */
class PizzaMargheritaFactory implements PizzaFactory
{
public function makePizza()
{
return new PizzaMargherita();
}
}
class PizzaMargherita implements Pizza
{
public function getType()
{
return 'Margherita';
}
}
/* Applicatie bakt een Pizza Margherita */
$factory = new PizzaMargheritaFactory();
$pizza = $factory->makePizza();
echo $pizza->getType();
?>
/* Interfaces voor pizzafabriek en pizza */
interface PizzaFactory
{
public function makePizza();
}
interface Pizza
{
public function getType();
}
/* Implementaties voor een Pizza Margherita */
class PizzaMargheritaFactory implements PizzaFactory
{
public function makePizza()
{
return new PizzaMargherita();
}
}
class PizzaMargherita implements Pizza
{
public function getType()
{
return 'Margherita';
}
}
/* Applicatie bakt een Pizza Margherita */
$factory = new PizzaMargheritaFactory();
$pizza = $factory->makePizza();
echo $pizza->getType();
?>
Om het topic wat completer te maken zou ik mijn vorm hier neer zetten, de Service container:
Code (php)
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
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
<?php
$container = new Container();
$container->set('pizzaria', function($c) {
return new Pizzaria();
});
$container->set('pizza', function($c) {
if (!isset($c->get('pizza.type')) {
// ... error
}
switch ($c->get('pizza.type')) {
case 'salami':
$pizza = new SalamiPizza();
break;
// ...
};
return $c->get('pizzaria')->getOven()->makePizza($pizza);
});
$container->set('service.waiter', function($c) {
$waiter = $c->get('service')->getFreeWaiters();
return $waiter[0]; // krijg de eerste vrije ober
});
// gebruik
$container->set('pizza.type', 'salami');
$pizza = $container->get('pizza');
if ($pizza->isBaked()) {
$container->get('service.waiter')->serve($pizza);
}
?>
$container = new Container();
$container->set('pizzaria', function($c) {
return new Pizzaria();
});
$container->set('pizza', function($c) {
if (!isset($c->get('pizza.type')) {
// ... error
}
switch ($c->get('pizza.type')) {
case 'salami':
$pizza = new SalamiPizza();
break;
// ...
};
return $c->get('pizzaria')->getOven()->makePizza($pizza);
});
$container->set('service.waiter', function($c) {
$waiter = $c->get('service')->getFreeWaiters();
return $waiter[0]; // krijg de eerste vrije ober
});
// gebruik
$container->set('pizza.type', 'salami');
$pizza = $container->get('pizza');
if ($pizza->isBaked()) {
$container->get('service.waiter')->serve($pizza);
}
?>
Ward, dat van jou gaat zijn nut voor bij. Het Factory pattern komt voor uit het feit dat je het geen dat variabel is moet weghalen uit het geen dat constant blijft. Niet door iets constants in de Factory te stoppen.
Gewijzigd op 05/11/2012 14:39:33 door Wouter J