begin process at 2008 05 16 04:30:11
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 [2]


Information sur le tutorial

Catégorie :Class et Objet ( POO ) Date de création : 31/08/2007 18:57:29 Vu : 4 405 fois

Note :
Aucune note

Commentaire sur cette source (18)
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 ! ;-)

Voici la suite de cette trilogie (au moins) : la partie 2

Tutorial

DESIGN PATTERNS (ou MOTIFS DE CONCEPTION). PART 2


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!

J'ai commencé dans la 1ère partie par les design patterns de type CREATION. Ce tuto va s'occuper des design patterns de type STRUCTUREL.



--STRUCTUREL--



COMPOSITE

Le composite permet de manipuler un groupe d'objets au fonctionnement similaire comme s'il s'agissait d'un seul objet.

Il comprend :

Le composant, une interface abstraite définissant le comportement, et déclare l'accès aux composants enfants (feuilles)

Le composite : stocke les feuilles et implémente leur gestion

La feuille : objet de la composition

L'exemple que je vais vous soumettre est une simulation d'application graphique : nous allons composer un graphique fait de plusieurs

éléments graphiques, puis nous allons tous les dessiner en un seul appel.

Cet exemple utilise une classe interne PHP5 de la SPL : SplObjectStorage, objet très pratique, et je profite donc de l'occasion pour vous le présenter

en action, même s'il n'est pas nécessaire au motif (mais son fonctionnement l'est, par contre).

Pour rappel, voici la doc de la SPL : http://www.php.net/~helly/php/ext/spl/


<?php

/**

* On redéfinit la classe SplObjectStorage au cas elle n'existe pas, et SURTOUT pour que vous voyiez son fonctionnement.

*/

if (!class_exists ('SplObjectStorage')) {

    /**

     * Interface SplObjectStorage

     * @author: php

     *

     */

    class SplObjectStorage implements Iterator, Countable {

        

        /**

         * Array of objects

         *

         * @var array

         */

        private $storage = array();

        /**

         * Array of objects index

         *

         * @var int

         */

        private $index = 0;

    

        /**

         * public function rewind

         * Rewind array pointer

         *

         * @Return void

         */

        public function rewind() {

            rewind($this->storage);

        }

    

        /**

         * public function valid

         * Checks if current loop iteration is valid

         *

         * @Return boolean

         */

        public function valid() {

            return key($this->storage) !== false;

        }


        /**

         * public function key

         * Returns current key

         *

         * @Return int

         */

        public function key() {

            return $this->index;

        }

        

        /**

         * public function current

         * Returns current array entry

         *

         * @Return object

         */

        public function current() {

            return current($this->storage);

        }

        

        /**

         * public function next

         * Iterates to the next entry

         *

         * @Return void

         */

        public public function next() {

            next($this->storage);

            $this->index++;

        }

        

        /**

         * public function count

         * Returns the number of entries in the array of objects

         *

         * @Return int

         */

        public function count() {

            return count($this->storage);

        }

        

        /**

         * public function contains

         * Checks if an object is in the collector array

         *

         * @Param object $obj : object to check

         * @Return boolean

         */

        public function contains($obj) {

            if (is_object($obj)) {

                foreach($this->storage as $object) {

                    if ($object === $obj) {

                        return true;

                    }

                }

            }

            return false;

        }

        

        /**

         * public function attach

         * Attaches an object to the collector array

         *

         * @Param object $obj : object to attach

         * @Return void

         */

        public function attach($obj) {

            if (is_object($obj) && !$this->contains($obj)) {

                $this->storage[] = $obj;

            }

        }

        

        /**

         * public function detach

         * Detaches an object to the collector array

         *

         * @Param object $obj : object to detach

         * @Return void

         */

        public function detach($obj) {

            if (is_object($obj)) {

                foreach($this->storage as $idx => $object) {

                    if ($object === $obj) {

                        unset($this->storage[$idx]);

                        $this->rewind();

                        return;

                    }

                }

            }

        }

    }

}

/**

* Notre interface : le COMPOSANT

*

*/

interface graphic {

    public function draw();

}


/**

* Notre COMPOSITE!

*

*/

class compositeGraphic implements graphic {

    private $aGraphics;

    

    public function __construct() {

        $this ->aGraphics = new SplObjectStorage;

    }

    

    public function draw() {

        foreach ($this ->aGraphics as $oGraph) {

            $oGraph ->draw();

        }

    }

    

    public function attach(graphic $oGraph) {

        $this ->aGraphics ->attach($oGraph);

    }


    public function detach(graphic $oGraph) {

        $this ->aGraphics ->detach($oGraph);

    }

}


/**

* Notre FEUILLE

*

*/

class ellipse implements graphic {

    public function draw() {

        echo 'Ellipse'."\r\n";

    }

}


/**

* Une autre FEUILLE

*

*/

class triangle implements graphic {

    public function draw() {

        echo 'Triangle'."\r\n";

    }

}


try {

    /**

     * On crée plusieurs ellipses et triangles

     */

    $ellipse1 = new ellipse;

    $ellipse2 = new ellipse;

    $ellipse3 = new ellipse;

    

    $triangle1 = new triangle;

    $triangle2 = new triangle;    

    /**

     * On crée plusieurs compositeGraphic

     */

    $graf = new compositeGraphic;

    $graf1 = new compositeGraphic;

    $graf2 = new compositeGraphic;

    

    /**

     * On attache 2 ellipses et 1 triangle au compositeGraphic 1

     */

    $graf1 ->attach($ellipse1);

    $graf1 ->attach($ellipse2);

    $graf1 ->attach($triangle1);

    

    /**

     * On attache la 3ème ellipse et le deernier triangle au compositeGraphic 2

     */

    $graf2 ->attach($ellipse3);

    $graf2 ->attach($triangle2);

    

    /**

     * On attache les 2 compositeGraphic précédents au 3ème

     */

    $graf ->attach($graf1);

    $graf ->attach($graf2);

    

    /**

     * On trace notre grahique complet !

     */

    $graf ->draw();

} catch (Exception $e) {

    echo $e;

}

?>



PROXY

Le proxy se substitue à une autre classe. Il implémente la même interface (et une seule interface) que cette classe.

Il contrôle l'accès aux méthodes de la classe substituée. Il peut aussi être utilisée pour simplifier ou optimiser l'utilisation

de la classe substituée.

Cette fois, 2 exemples. Le premier pour illustrer le contrôle d'accès, le second pour illustrer l'optimisation :

Le 1er exemple, donc, simule l'accès à un compte utilisateur. La classe user possède un numéro de compte auquel on ne doit

pas pouvori accéder sans le mot de passe adéquat. Le proxy vérifie que cette condition soit remplie, et autorise l'accès au

numéro de compte uniquement dans ce cas-là :


<?php

/**

* L'interface

*

*/

interface iUser {

    public function getUserAccount();

}


/**

* La classe user qui va être substituée

* Possède un numéro de compte auquel on ne doit pas accéder sans le mot de passe!

*

*/

class user implements iUser {

    private $sAccount = '12345';

    

    public function __construct() {

        echo 'User initialisé<br />';

    }

    

    public function getUserAccount() {

        return $this ->sAccount;

    }

}


/**

* Le proxy se substituant à user.

* Le proxy va vérifier la validité du mot de passe avant de retourner le numéro de compte de l'utilisateur

*

*/

class userProxyProtection implements iUser {

    private $sPassword;

    private $oUser;

    

    public function __construct($sPwd) {

        $this ->sPassword = $sPwd;

        $this ->oUser = new user();

    }

    

    public function getUserAccount() {

        /**

         * Par commodité, je fixe ici le mot de passe demandé. En réalité, on passerait par une interrogation d'une base de données par exemple

         */

        if ($this ->sPassword !== 'motdepasse') {

            throw new Exception ('Mot de passe invalide');

        }

        return $this ->oUser ->getUserAccount();

    }

}


try {

    $oUser = new userProxyProtection('motdepasse');

    echo $oUser ->getUserAccount();

} catch (Exception $e) {

    echo $e;

}

?>


Le 2d exemple simule l'accès à de gros fichiers. On veut afficher leur contenu; il faut donc les charger.

Le proxy va s'occuper de s'assurer que l'on ne charge les fichiers que si c'est nécessaire (si on ne l'a pas déjà chargé et

uniquement si on veut l'afficher):


<?php

/**

* L'interface commune

*

*/

interface iBigFile {

    public function displayFile();

}


/**

* La classe bigFile qui charge et affiche un gros fichier

*

*/

class bigFile implements iBigFile {

    private $sFileName;

    

    public function __construct($sFileName) {

        $this ->sFileName = $sFileName;

        echo 'Charge ',$this ->sFileName, '<br />';

    }

    

    public function displayFile() {

        echo 'Affiche ', $this ->sFileName, '<br />';

    }

}


/**

* Le proxy qui va se substituer à bigFile et ne permettre le chargement d'un fichier QUE si c'est nécessaire (s'il n'a pas été déjà chargé et si l'on demande son affichage)

*

*/

class proxyBigFile implements iBigFile {

    private $sFileName;

    private $oFile;

    

    public function __construct($sFileName) {

        $this ->sFileName = $sFileName;

    }

    

    public function displayFile() {

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

            $this ->oFile = new bigFile($this ->sFileName);

        }

        $this ->oFile ->displayFile();

    }

}


$aFiles = array (new proxyBigFile('fichier1'), new proxyBigFile('fichier2'), new proxyBigFile('fichier3'));

$aFiles[0] ->displayFile();

$aFiles[1] ->displayFile();

$aFiles[0] ->displayFile(); // ce fichier (fichier1) ne sera pas rechargé puisqu'il l'a déjà été!

// A noter que le fichier3 n'a pas du tout été chargé puisqu'on ne demande pas son affichage!

?>



DELEGATION

Le Delegation est une technique avec laquelle un objet possède un comportement mais délègue la responsabilité de

l'implémentation de ce comportement à un autre objet.

A noter que cela résulte souvent en une perte de performances, là où l'on gagne en lisibilité.

Ce design pattern est très utile dans les cas où l'on a une multitude de classe héritées dont les comportements semblent

très proches mais sont en réalité assez différents : il en ressort alors avec une programmation classique des classes très

complexes héritant de-ci de-là et devenant incompréhensibles.

Voici un exemple simple simulant le comportement des hommes et des femmes :


<?php

/**

* Une interface pour décrire les comportements de notre package de classes

*

*/

interface iPeople {

    public function sayName();

    public function sayHello();

    public function drink();

    public function watchTv();

}


/**

* Une classe abstraite parente

* Cette classe va aller chercher les comportements de ses enfants

* Elle délègue...

*

*/

abstract class people implements iPeople {

    protected $sName;

    protected $drinkBehaviour;

    protected $watchTvBehaviour;

    

    public function __construct($sName) {

        $this ->sName = $sName;

    }

    

    public function sayName() {

        echo 'Mon nom est ', $this ->sName,'<br />';

    }

    

    public function sayHello() {

        echo 'Hello !<br />';

    }

    

    public function drink() {

        $this ->drinkBehaviour ->drink();

    }

    

    public function watchTv() {

        $this ->watchTvBehaviour ->watchTv();

    }


    public function setSayNameBehaviour($sayNameBehaviour) {

        $this ->sayNameBehaviour = $sayNameBehaviour;

    }

    

    public function setSayHelloBehaviour($sayHelloBehaviour) {

        $this ->sayHelloBehaviour = $sayHelloBehaviour;

    }

    

    public function setDrinkBehaviour($drinkBehaviour) {

        $this ->drinkBehaviour = $drinkBehaviour;

    }

    

    public function setWatchTvBehaviour($watchTvBehaviour) {

        $this ->watchTvBehaviour = $watchTvBehaviour;

    }

}


/**

* Une interface déclarant un comportement

*

*/

interface drinkBehaviour {

    public function drink();

}


/**

* Un comportement associé à une méthode drink()

* C'est un delegué

*

*/

class drinkBeer implements drinkBehaviour {

    public function drink() {

        echo 'Je bois de la bière'.'<br />';

    }

}


/**

* Un autre comportement, différent de celui ci-dessus, associé aussi à la méthode drink(), destiné à une autre classe

* C'est aussi un delegué

*/

class drinkCosmopolitan implements drinkBehaviour {

    public function drink() {