Een Factory als Helper Class (statisch)

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Tim S

Tim S

04/11/2012 20:28:56
Quote Anchor link
llo,

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
 
PHP hulp

PHP hulp

26/04/2024 02:49:37
 
Wouter J

Wouter J

04/11/2012 21:17:47
Quote Anchor link
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)
 
Frank Nietbelangrijk

Frank Nietbelangrijk

04/11/2012 21:26:48
Quote Anchor link
Tussen de twee methodes zit een levensgroot verschil Tim.

zie het voorbeeld appel en peer (appels met peren vergelijken ;-)

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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

?>
Gewijzigd op 04/11/2012 22:02:00 door Frank Nietbelangrijk
 
Wouter J

Wouter J

04/11/2012 21:30:41
Quote Anchor link
Frank, er zit ook een levensgroot verschil tussen de vraag van Tim in jou antwoord. Daar vroeg hij namelijk niet naar. Tim vroeg zich af waarom we niet de statische methode gebruiken voor een factory.

Tevens hoort jouw code niet te kloppen, je bent de static keywords vergeten.
Gewijzigd op 04/11/2012 21:31:16 door Wouter J
 
Frank Nietbelangrijk

Frank Nietbelangrijk

04/11/2012 21:50:38
Quote Anchor link
japs één dan bij de method. ging er al van uit dat ObjectFactory::makeNewObject() niet kon.. static toegevoegd.
Gewijzigd op 04/11/2012 22:09:23 door Frank Nietbelangrijk
 
Tim S

Tim S

05/11/2012 12:21:38
Quote Anchor link
Bedankt voor de snelle reacties!

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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class User{

  public $name;

  public function construct($name){
    $this->name = $name;
  }

}


$user = new User();

?>



B. Factory Method
Zie ik ook veel:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

class User{

  public static function create(){
   return new self();
  }

}


$user = User::create();

?>


C. Factory
Waar mijn vraag over gaat:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?php

class User{

  public $name;

  public function construct($name){
    $this->name = $name;
  }

}


class UserFactory{

  public static function newTimUser(){
    return new User("Tim");
  }


  public function newOtherUser(){
    return new User("Other");
  }

}


?>


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:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php

$factory
= new UserFactory();
$user = $factory->newOtherUser();

?>


C.2. Zoals ik het graag zou zien:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php

$user
= UserFactory::newTimUser();

?>



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)
 
Kris Peeters

Kris Peeters

05/11/2012 12:44:54
Quote Anchor link
Ja, ik snap je vraag wel.

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"
 
Wouter J

Wouter J

05/11/2012 13:04:01
Quote Anchor link
Het Factory pattern is in geen manier bedacht voor het geen jij hem gebruikt. Een factory gebruik je als je met 1 mogelijkheid meerdere objecten zou kunnen aanmaken. Bij bijv. een pizzaria:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
switch ($_POST['pizza']) {
    case
'salami':
        $pizza = new SalamiPizza();
        break;

    case
'hawai':
        $pizza = new HawaiPizza();
        break;

    default:

        $pizza = new MagaritaPizza();
}

?>

Dit verplaats je in OO naar een factory:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

class Pizzaria
{
    public function createPizza($type)
    {

        switch ($_POST['pizza']) {
            case
'salami':
                $pizza = new SalamiPizza();
                break;

            case
'hawai':
                $pizza = new HawaiPizza();
                break;

            default:

                $pizza = new MagaritaPizza();
        }
    }
}

?>

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.
 
Gerard M

Gerard M

05/11/2012 13:40:47
Quote Anchor link
Wat jij graag wilt zien, kan ook gewoon. Je kan het zelfs met een singleton combineren zodat je een soort van "static constructor" krijgt.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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");

?>
Gewijzigd op 05/11/2012 14:34:08 door Gerard M
 
Wouter J

Wouter J

05/11/2012 13:49:14
Quote Anchor link
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.
 
Gerard M

Gerard M

05/11/2012 14:27:00
Quote Anchor link
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.


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.
 
Ward van der Put
Moderator

Ward van der Put

05/11/2012 14:33:35
Quote Anchor link
Interessant topic! Een factory pattern voor een pizzeria kan er echter ook anders uitzien. Hebben jullie op- en aanmerkingen bij de volgende aanpak?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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();
?>
 
Wouter J

Wouter J

05/11/2012 14:37:35
Quote Anchor link
Het feit dat je telkens dezelfde instance krijgt is voor mij al het punt om ervan af te stappen, maar ik kan begrijpen dat beginners zoals de TS dit waarschijnlijk wel een mooi pattern vinden.

Om het topic wat completer te maken zou ik mijn vorm hier neer zetten, de Service container:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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);
}

?>





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
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.