begin process at 2008 05 16 04:28:51
1 173 209 membres
51 nouveaux aujourd'hui
13 970 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

DESIGN PATTERNS [1]


Information sur le tutorial

Catégorie :Class et Objet ( POO ) Date de création : 21/08/2007 15:24:11 Vu : 5 852 fois

Note :
10 / 10 - par 1 personne
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (9)
Ajouter un commentaire et/ou une note

Description

J'ai décidé d'écrire ce tuto sur les design patterns car si l'on trouve de nombreuses références à ces méthodes de programmation sur le net,
la plupart du temps elles sont en anglais. Or, tout le monde n'est pas à l'aise avec la langue de Shakespeare ;-)
Je ne couvrirai pas l'exhaustivité des design patterns existants, mais je vais tâcher de me concentrer sur les plus fréquemment utilisés.
Les exemples de code seront écrits en PHP5, parce que...ben parce qu'on en est bientôt à PHP6 alors il serait temps
d'abandonner PHP4 ! ;-)

Tutorial

DESIGN PATTERNS (ou MOTIFS DE CONCEPTION). PART 1


INTRODUCTION

J'ai décidé d'écrire ce tuto sur les design patterns car si l'on trouve de nombreuses références à ces méthodes de programmation sur le net,

la plupart du temps elles sont en anglais. Or, tout le monde n'est pas à l'aise avec la langue de Shakespeare ;-)

Je ne couvrirai pas l'exhaustivité des design patterns existants, mais je vais tâcher de me concentrer sur les plus fréquemment utilisés.

Les exemples de code seront écrits en PHP5, parce que...ben parce qu'on en est bientôt à PHP6 alors il serait temps

d'abandonner PHP4 ! ;-)



UN DESIGN PATTERN, QU'EST-CE DONC ??

Depuis que le monde est monde (ou presque...), les programmeurs se sont heurtés à des problèmes récurrents. Ils ont donc

réflêchi, et ont mis en place des méthodes pour résoudre ces problèmes. Les design patterns sont ces méthodes.

Vous avez probablement déjà utilisé des design patterns sans le savoir! Par vous-même, ou en ayant copié un bout de code.

Mais il est bon de connaître ces méthodes et de les utiliser de manière académique : votre code en deviendra plus lisible

pour les autres programmeur, au fait de ces méthodes. De plus, à quoi bon réinventer la roue ? Des programmeurs chevronnés

ont eu, avant vous, les mêmes problèmes que vous, y ont réflêchi, et ont trouvé des parades : ces méthodes sont éprouvées, car

elles sont été retournées dans tous les sens depuis bien des années.



LE VIF DU SUJET!

Je vais commencer par des design patterns que vous connaissez sans doute déjà, car certains sont expliqués dans la doc de PHP5,

sur php.net. Ils font partie des design patterns de type création. Je continuerai les autres types de design patterns (de type

structure et comportement) dans d'autres tutos.



--CREATION--



SINGLETON

Le singleton sert à restreindre l'instanciation d'une classe à 1 seul objet. En clair, en utilisant le singleton, si vous

tentez d'instancier 2 fois une classe, 1 seul objet vous sera renvoyé. Ce design pattern peut être très utile dans de nombreux cas,

afin d'éviter des doublons inutiles, et donc des pertes de ressources.

Vous trouverez un exemple de singleton sur php.net : http://fr.php.net/manual/fr/language.oop5.patterns.php

Voici comment un singleton très simple peut se présenter :


<?php

class single {

    

    /**

     * Propriété privée stockant une instance de la classe si single::getInstance() est appelé

     *

     * @var single

     */

    private static $_instance;

    

    /**

     * Constructeur privé puisqu'on ne veut pas pouvoir instancier single de cette manière. Il faut passer par single::getInstance()

     *

     */

    private function __construct () {

        echo 'je suis une instance de single';

    }

    

    /**

     * Méthode implémentant le design pattern singleton

     *

     * @return single

     */

    public static function getInstance () {

        if (!isset (self::$_instance)) {

            self::$_instance = new single();

        }

        return self::$_instance;

    }

    

    /**

     * Méthode publique quelconque

     *

     */

    public function doSomething () {

        echo 'I am doing something!';

    }

    

    /**

     * Si l'on ne veut pas pouvoir instancier single de façon normale, on ne doit pas non plus pouvoir le cloner.

     *

     */

    public function __clone () {

        throw new Exception ('Le clonage de single n\'est pas autorisé');

    }

}


//$o = new single (); // va échouer car le constructeur est privé

$o = single::getInstance ();

$o ->doSomething ();

$o2 = clone ($o); // va lancer une exception grâce à single::__clone()

?>



FACTORY

La factory, ou usine, est responsable de la "fabrication" d'objets. Il est très pratique dans le cadre d'un ensemble de

classes abstraites et concrètes, souvent semblables, mais au fonctionnement légèrement différent et qui s'applique dans des

cas précis. Par exemple, une classe d'abstraction de base de données pourrait implémenter une usine : l'usine instanciera

la classe dédiée à mysql ou à mssql selon les cas.

Un exemple se trouve sur php.net : http://fr.php.net/manual/fr/language.oop5.patterns.php

Voici comment une factory très simple peut se présenter :


<?php

abstract class animalFactory {

    

    /**

     * Factory !

     *

     * @param string $sType

     * @return object

     */

    public static function factory ($sType) {

        if (!class_exists ($sType, true) ) {

            throw new Exception ($sType.' n\a pas été trouvé');

        }

        return new $sType;

    }

}


interface animal {

    public function say();    

}


class dog implements animal {

    

    public function say () {

        echo 'wouf';

    }

}


class cat implements animal {


    public function say () {

        echo 'miaou';

    }

}


try {

    $o = animalFactory::factory('cat');

    $o ->say();

} catch (Exception $e) {

    echo $e;

}

?>



SINGLETON FACTORY?

Et oui, pourquoi ne pas mixer les deux ? Les design patterns ne sont pas forcément destinés à être utilisés séparément!

Bien au contraire...

Voici un exemple un peu plus complexe :

Il se compose d'une classe parente abstraite qui va jouer le rôle à la fois d'usine, ET de contenant de singletons.

En fait, il s'agit d'un multiton : on peut stocker ici 1 et 1 seule référence à x classes.

Cet exemple utilise l'API de Réflexion afin de vérifier que les classes demandées soient valides (étendant la classe animal parente).

Vous trouverez plus d'infos sur cette API ici : http://fr2.php.net/manual/fr/language.oop5.reflection.php


<?php

abstract class animal {

    

    public static $_aInstance;

    

    protected function __construct () {

        // interdit!

    }

    

    /**

     * Factory !

     * Plus complexe, on vérifie cette fois que la classe demandée soit une classe valide (étendant animal) via la Réflexion

     *

     * @param string $sType

     * @return object

     */

    public static function singletonFactory ($sType) {

        $o = new ReflectionClass($sType);

        if (!$o ->isSubclassOf(__CLASS__)) {

            throw new Exception ($sType.' n\'est pas une classe enfant de '.__CLASS__);

        }

        if (!isset (self::$_aInstance[$sType])) {

            self::$_aInstance[$sType] = new $sType;

        }

        return self::$_aInstance[$sType];

    }

    

    /**

     * Methode pour vérifier notre tableau d'instance

     *

     * @return array

     */

    public static function getObject () {

        return self::$_aInstance;    

    }

    

    /**

     * Méthode abstraite affichant le cri de l'animal concerné

     *

     */

    abstract public function say();    

}


class dog extends animal {

    

    public function say () {

        echo 'wouf';

    }

}


class cat extends animal {

    

    public function say () {

        echo 'miaou';

    }

}


try {

    $o = animal::singletonFactory('dog');

    $o ->say();

    $o2 = animal::singletonFactory('cat');

    $o2 ->say();

/**

    * Cet objet $o3 sera exactement le même que $o!

    */

    $o3 = animal::singletonFactory('dog');

    $o3 ->say();

/**

* On vérifie tout de suite cette affirmation...

*/

    echo '<pre>',print_r (animal::getObject()),'</pre>';

} catch (Exception $e) {

    echo $e;

}

?>



BUILDER

Le builder, ou monteur, est destiné à séparer le construction d'un objet de sa définition,

de manière à toujours utiliser le même processus de construction.

Il se compose de 4 entités :

Un monteur abstrait, définissant les méthodes abstraites des monteurs concrêts

Un (ou X) monteur concrêt implémentant le constructeur abstrait et construisant effectivement les produits

Un directeur qui se charge d'agencer correctement la construction des produits

Un produit, qui est l'objet à construire

Voici un exemple de builder :


<?php

/**

* Notrre sandwich de base, le PRODUIT

*

*/

class sandwich {

    /**

     * Ses propriétés

     *

     * @var string

     */

    private $sPain;

    private $sContenu;

    private $sSauce;

    

    /**

     * Setter pour le pain

     *

     * @param string $sPain

     */

    public function setPain ($sPain) {

        $this ->sPain = $sPain;

    }

    

    /**

     * Setter pour le contenu

     *

     * @param string $sContenu

     */

    public function setContenu ($sContenu) {

        $this ->sContenu = $sContenu;

    }

    

    /**

     * Setter pour la sauce

     *

     * @param string $sSauce

     */

    public function setSauce ($sSauce) {

        $this ->sSauce = $sSauce;

    }

    

    /**

     * La méthode magique __toString qui va se charger d'afficher une description du sandwich créé

     *

     * @return string

     */

    public function __toString() {

        $sReturn = 'Votre sandwich : '.$this ->sContenu.' dans '.$this ->sPain.' avec '.$this ->sSauce;

        return $sReturn;

    }

    

}


/**

* Notre MONTEUR ABSTRAIT

*

*/

abstract class sandwichBuilder {

    /**

     * Le PRODUIT qu'il va créer

     *

     * @var sandwich

     */

    protected $oSandwich;

    

    /**

     * Getter pour récupérer le PRODUIT créé

     *

     * @return sandwich

     */

    public function getSandwich () {

        return $this ->oSandwich;

    }

    

    /**

     * Initialisation de la création du PRODUIT

     *

     */

    public function createNewSandwich() {

        $this ->oSandwich = new sandwich;

    }

    

    /**

     * Séquence de construction

     *

     */

    abstract public function addPain();

    abstract public function addContenu();

    abstract public function addSauce();

}


/**

* Un MONTEUR CONCRET!

*

*/

class jambonBeurre extends sandwichBuilder {

    

    public function addPain() {

        $this ->oSandwich ->setPain('baguette');

    }


    public function addContenu() {

        $this ->oSandwich ->setContenu('jambon');

    }

    

    public function addSauce() {

        $this ->oSandwich ->setSauce('beurre');

    }

}


/**

* Et un autre...

*

*/

class tomates extends sandwichBuilder {

    

    public function addPain() {

        $this ->oSandwich ->setPain('pain complet');

    }


    public function addContenu() {

        $this ->oSandwich ->setContenu('tomates');

    }

    

    public function addSauce() {

        $this ->oSandwich ->setSauce('huile d\'olive');

    }

}


/**

* Notre DIRECTEUR :-) Il va se charger de gérer correctement les séquences nécessaires pour la construction

*

*/

class serveur {

    /**

     * Le MONTEUR CONCRET dont il va se servir

     *

     * @var sandwichBuiledr

     */

    private $oSandwichBuilder;

    

    /**

     * Setter qui va initialiser le MONTEUR CONCRET désiré

     *

     * @param sandwichBuilder $sandwichBuilder

     */

    public function setSandwichBuilder (sandwichBuilder $sandwichBuilder) {

        $this ->oSandwichBuilder = new $sandwichBuilder;

    }

    

    /**

     * Getter, retournant le PRODUIT créé par répercussion

     *

     * @return sandwich

     */

    public function getSandwich () {

        if (is_null ($this ->oSandwichBuilder)) {

            throw new Exception (__METHOD__.'() : aucun sandwichbuilder défini');

        }

        return $this ->oSandwichBuilder ->getSandwich();

    }

    

    /**

     * La séquence de construction du PRODUIT

     *

     */

    public function createSandwich() {

        if (is_null ($this ->oSandwichBuilder)) {

            throw new Exception (__METHOD__.'() : aucun sandwichbuilder défini');

        }

        $this ->oSandwichBuilder ->createNewSandwich();

        $this ->oSandwichBuilder ->addPain();

        $this ->oSandwichBuilder ->addContenu();

        $this ->oSandwichBuilder ->addSauce();

    }

}

try {

    $oJambonBeurre = new jambonBeurre;

    $oTomates = new tomates;

    $oServeur = new serveur;

    $oServeur ->setSandwichBuilder ($oJambonBeurre);

    $oServeur ->createSandwich();

    echo $oServeur ->getSandwich();

    echo '<br />';

    $oServeur ->setSandwichBuilder ($oTomates);

    $oServeur ->createSandwich();

    echo $oServeur ->getSandwich();

} catch (Exception $e) {

    echo $e;

}

?>



PROTOTYPE

Le prototype sert à optimiser la création d'instances complexes. Au lieu de créer une nouvelle instance de la classe,

on va cloner la 1ère instance créée. Le clone possède toutes les propriétés de l'original.

On utilise en PHP5 la méthode magique __clone() pour cela : http://fr2.php.net/manual/fr/language.oop5.cloning.php

Voici un exemple très simple :


<?php

/**

* PROTOTYPE abstrait

*

*/

abstract class cookie {

    protected $iInstance;

    protected $sName;

    

    abstract public function setName($sName);

    abstract public function __clone ();

}


/**

* PROTOTYPE concrêt

*

*/

class chocolateCookie extends cookie {

    protected static $iInstances = 0;

    

    public function setName ($sName) {

        $this ->sName = $sName;

    }

    

    public function __clone () {

        $this ->iInstance = ++self::$iInstances;

    }

}


/**

* PROTOTYPE concrêt

*

*/

class macadamiaCookie extends cookie {

    protected static $iInstances = 0;


    public function setName ($sName) {

        $this ->sName = $sName;

    }

    

    public function __clone () {

        $this ->iInstance = ++self::$iInstances;

    }

}


/**

* La classe utilisatrice

*

*/

class cookieMachine {

    private $oCookie;

    public static $aCookies;

    

    public function setCookie(cookie $oCookie) {

        $this ->oCookie = $oCookie;

    }

    

    public function makeCookie () {

        self::$aCookies[] = clone $this ->oCookie;

    }

}


/**

* On crée une 1ère instance de chocolateCookie

*/

$oChocCookie = new chocolateCookie;

/**

* Et on lui attribue un nom!

*/

$oChocCookie ->setName ('Chocolate Cookie !');


/**

* Idem avec une instance de macadamiaCookie

*/

$oMacCookie = new macadamiaCookie;

$oMacCookie ->setName ('Macadamia Cookie !');


/**

* On crée une machine à faire les cookies (miam)

*/

$oMachine = new cookieMachine;


$oMachine ->setCookie($oChocCookie);

for ($i = 0; $i < 5; $i ++) {

    $oMachine ->makeCookie();

}


$oMachine ->setCookie($oMacCookie);

for ($i = 0; $i < 10; $i ++) {

    $oMachine ->makeCookie();

}


/**

* Et oui, tous les cookies clonés ont le même nom que le cookie original, alors que l'on n'est pas repassé par cookie::setName()! Par contre, nous avons explicitement modifié la propriété cookie::iInstance dans la méthode cookie::__clone(), donc celle-ci est différente à chaque fois.

*/

print_r (cookieMachine::$aCookies);

?>



21 août 2007 15:27:10 :
Remise en page...
24 août 2007 11:00:25 :
Remise en page, test avec word 2007
  • signaler à un administrateur
    Commentaire de craso le 25/08/2007 16:48:52

    j'ai hâte de lire les autres tutos :)

  • signaler à un administrateur
    Commentaire de malalam le 25/08/2007 19:46:18 administrateur CS