Archives de catégorie : PHP

PHP : un exemple simple de design pattern Decorator

Le design pattern Decorator (en français, décorateur) a pour but d’étendre les fonctionnalités d’un objet grâce à l’utilisation de l’héritage. Mon père m’a toujours dit que d’un âne, on ne pouvait pas faire un cheval de course; je vais m’employer à lui donner tort ! Voici un diagramme de classe qui sert de base à notre exemple, je l’ai réalisé avec l’outil ArgoUML sur GNU/LInux Debian :

Diagrammedeclasses

Au sommet de notre diagramme trône fièrement la classe abstraite Equide : elle possède une variable d’instance protégée de type chaîne de caractères qui stocke une description très sommaire de l’équidé ainsi que deux méthodes dont une (donne Description) est abstraite. Voici son code en détail :


abstract class Equide {
    protected $_description = 'équidé commun';
    abstract public function courir();

    public function donneDescription() {
        return $this->_description;
    }
}

Ce super-type est dérivé en deux classes concrètes :


class AneSauvage extends Equide {
    public function __construct() {
        $this->_description = 'âne sauvage';
    }

    public function courir() {
        echo "Il m'arrive de courir à l'occasion...";
    }
}

class AneDomestique extends Equide {
    public function __construct() {
        $this->_description = 'âne domestique';
    }

    public function courir() {
        echo "Si vraiment on m'y oblige, je trotte...";
    }
}

La méthode courir, signalée abstraite dans la mère est implémentée dans les filles et la valeur par défaut stockée dans la variable d’instance _description est écrasée avec une valeur un peu plus censée lors de la construction de l’objet.

La nouveauté arrive maintenant…


abstract class DecorateurEquide extends Equide {
    protected $_equide;

    public function __construct(Equide $equide) {
        $this->_equide = $equide;
    }
}

class ChevalDeCourse extends DecorateurEquide {

    public function donneDescription() {
        return $this->_equide->donneDescription() .
               ' qui court très très vite !' . PHP_EOL;
    }

    public function courir() {
        return $this->_equide->courir() .
               ' et maintenant je galope tel un cheval de course !' .
               PHP_EOL;
    }
}

Notre classe décorateur est DecorateurEquide; voyez-le par vous-mêmes, c’est une classe abstraite…il faudra donc la dériver. Cette classe prend en composition un objet de la classe Equide, puisque c’est précisément cet objet qu’elle va décorer ! C’est lors de l’instanciation d’une de ses classes filles que l’on passera notre instance d‘Equide au constructeur.

La seule classe fille d’Equide dans notre exemple est ChevalDeCourse; elle ajoute des fonctionnalités aux méthodes courir et donneDescription qui proviennent de DecorateurEquide et donc de Equide. Dans ce cet exemple trivial ces fonctionnalités se résument à une simple chaîne de caractères.

Les décorateurs ont le même type que les objets qu’ils décorent, c’est la raison pour laquelle DecorateurEquide hérite d’Equide, pour en être un sous-type ! L’héritage n’est pas réalisé à des fins « comportementales » mais simplement pour des raisons de typage. J’ai mis la variable d’instance _equide et le constructeur dans DecorateurEquide mais j’aurais pu tout aussi bien laisser cette classe vide (ne m’en servir vraiment que pour le typage) et déporter ce code dans les classes concrètes qui spécialisent (et spécialiseront à terme) DecorateurEquide. C’est un choix de conception parmi d’autres…

Pour utiliser ce code :


$aneDomestique = new AneDomestique;
$cheval = new ChevalDeCourse($aneDomestique);

echo $cheval->donneDescription();
echo $cheval->courir();

Tu vois Papa, c’est bien la preuve que d’un âne, on peut faire un cheval de course !

smiling donkey

Source : blog Terapias Naturales

Le design pattern Factory Method en PHP

Le design pattern Factory Method est appelé en français fabrique; c’est un design pattern dit « de création » puisque le but de Factory Method est de créer des objets. La fabrique est utilisée pour réduire le couplage entre les classes; son but est qu’une classe cliente ne fasse plus des instanciations elle-même mais qu’elle passe par une autre classe qui connait le processus (potentiellement complexe) de la création d’un objet dans les détails.

Ouvrières chargées d'instancier des objets dans une factory method

Ouvrières chargées d’instancier des objets

Présentons la facture !

Soit une classe Facturation exposant une méthode declencher et qui constitue une facture en créant une entête, un corps et un pied de page sur lesquels elle va invoquer la méthode formater.

interface RubriqueInterface
{
    public function formater(): void; 
}

class Entete implements RubriqueInterface
{
    public function formater(): void
    {
        echo "Je formate mon entête".PHP_EOL;
    }
}

class Corps implements RubriqueInterface
{
    private $produits;

    public function __construct(array $produits)
    {
        $this->produits = $produits;
    }

    public function formater(): void
    {
        echo "Corps avec ".count($this->produits)." produits".PHP_EOL;
    }
}

class PiedPage implements RubriqueInterface
{
    public function formater(): void
    {
        echo "Je formate mon pied de page".PHP_EOL;
    }
}

class Facturation
{
    private $entete;

    private $corps;

    private $piedPage;

    public function __construct(array $produits)
    {
        $this->entete = new Entete();
        $this->corps = new Corps($produits);
        $this->piedPage = new PiedPage();
    }

    public function declencher(): void
    {
        $this->entete->formater();
        $this->corps->formater();
        $this->piedPage->formater();
    }
}

$facture = new Facturation([['nom' => 'Gourde', 'prix' => 9.99]]);
$facture->declencher();

Ici Facturation est très fortement couplée avec Entete, Corps et PiedPage puisque les instanciations sont faites directement par cette classe. Nous allons diminuer ce couplage en faisant appel à nos fabriques et leur factory methods. La factory method de notre fabrique abstraite est fabriquer (comme c’était prévisible…):

abstract class FabriqueFacture {
    abstract public function fabriquer();
}

class Facturation
{
    private $entete;

    private $corps;

    private $piedPage;

    public function __construct(array $produits)
    {
        $fabriqueEntete = new FabriqueEnteteFacture();
        $this->entete = $fabriqueEntete->fabriquer();

        $fabriqueCorps = new FabriqueCorpsFacture($produits);
        $this->corps = $fabriqueCorps->fabriquer();

        $fabriquePiedPage = new FabriquePiedPageFacture();
        $this->piedPage = $fabriquePiedPage->fabriquer();
    }

    public function declencher(): void
    {
        $this->entete->formater();
        $this->corps->formater();
        $this->piedPage->formater();   
    }
}

$facture = new Facturation([['nom' => 'Gourde', 'prix' => 9.99]]);
$facture->declencher();
 
class FabriqueEnteteFacture extends FabriqueFacture
{
    private $classeCible = 'Entete';

    public function fabriquer(): RubriqueInterface
    {
        return new $this->classeCible();
    }
}
 
class FabriqueCorpsFacture extends FabriqueFacture
{
    private $classeCible = 'Corps';

    private $produitsAFacturer;

    public function __construct(array $produits)
    {
        $this->produitsAFacturer = $produits;
    }

    public function fabriquer(): RubriqueInterface
    {
        return new $this->classeCible($this->produitsAFacturer);
    }
}
 
class FabriquePiedPageFacture extends FabriqueFacture
{
    private $classeCible = 'PiedPage';

    public function fabriquer(): RubriqueInterface
    {
        return new $this->classeCible();
    }
}

Alors oui, vous allez me dire « Oui mais, elle instancie des fabriques notre classe Facturation » et je ne le contesterai pas ! Facturation fait effectivement appel à des fabriques à qui elle délègue les instanciations. C’est là que réside le découplage !

Ce sont les classes concrètes dérivant FabriqueFacture qui auront la responsabilité de l’instanciation des classes qui constituent les différentes parties d’une facture : une fabrique de pieds de pages sait qu’elle doit instancier des pieds de pages. Souvenez-vous : une classe = une responsabilité ! Afin de vous faciliter la vie durant les tests unitaires, le mieux serait d’injecter directement ses dépendances au constructeur de Facturation, à savoir les 3 fabriques. C’est très simple.

class Facturation
{
    private $entete;

    private $corps;

    private $piedPage;

    public function __construct(FabriqueFacture $fabriqueEntete, 
        FabriqueFacture $fabriqueCorps,
        FabriqueFacture $fabriquePiedPage)
    {
        $this->entete = $fabriqueEntete->fabriquer();
        $this->corps = $fabriqueCorps->fabriquer();
        $this->piedPage = $fabriquePiedPage->fabriquer();
    }

    public function declencher(): void
    {
        $this->entete->formater();
        $this->corps->formater();
        $this->piedPage->formater();
    }
}

$produits = [['nom' => 'Gourde', 'prix' => 9.99]];

$facture = new Facturation(
  new FabriqueEnteteFacture(),
  new FabriqueCorpsFacture($produits),
  new FabriquePiedPageFacture()
);
$facture->declencher();

Résumons les unités de code en présence dans le design pattern Factory Method :

  • La classe cliente : Facturation
  • La fabrique abstraite : FabriqueFacture (notez qu’une interface aurait suffi, essayez !)
  • Les fabriques concrètes : FabriqueEnteteFacture, FabriqueCorpsFacture, FabriquePiedPageFacture
  • Les classes « produits » (ce qu’on veut obtenir au final) : Corps, Entete, PiedPage

PHP : petit exemple mêlant type-hint, abstraction et héritage.

Nous allons écrire un tout petit snippet pour mettre en évidence des concepts de base de la programmation orientée objet. Le but ici est d’utiliser l’abstraction, l’héritage, le typage (type-hint) et le principe de substitution de Liskov. Le tronc de notre arbre d’héritage est ici la classe abstraite Animal; elle ne possède qu’une seule fonction membre : communiquer.

Pourquoi communiquer est une fonction abstraite (un prototype, donc) dans Animal ? Tout simplement parce qu’un animal ne communique pas forcément comme un autre. Nous déléguons ici la responsabilité de l’implémentation de cette méthode aux classes filles d’Animal.

Nous avons ensuite deux classes concrètes Chameau et Belette qui elles détaillent la manière dont nos animaux communiquent: dès lors qu’elles dérivent Animal, elles sont tenues d’implémenter la méthode communiquer, ce qu’elles font, chacune à leur manière (le chameau blatère tandis que la belette…belote, et oui !).

Nous avons finalement une classe Communicateur, dont une variable d’instance privée et nommée _animal va contenir l’instance d’Animal sur laquelle nous allons opérer. Notez que nous avons forcé le type de paramètre en disant « Nous exigeons une instance de la classe Animal » dans le constructeur de cette classe. C’est une bonne chose que de ne pas accepter n’importe quoi

Souvenez-vous toujours de cette phrase de feu-Jon Postel :

Be liberal in what you accept, and conservative in what you send

Vous restez libéral dans ce que vous acceptez (tout en étant il faut le dire un peu conservateur aussi, puisque vous contraignez le type malgré tout), vous acceptez tout ce qui est un Animal : donc, tout ce qui dans l’arbre d’héritage se situe dans les branches (et les feuilles) partant du tronc qui est Animal.


abstract class Animal {
    abstract public function communiquer();
}

class Chameau extends Animal{
     public function communiquer() {
         echo "Je blatère", PHP_EOL;
     }
}

class Belette extends Animal{
    public function communiquer() {
        echo "Je belote", PHP_EOL;
    }
}

class Communicateur {

    private $_animal;

    public function __construct(Animal $animal) {
        $this->_animal = $animal;
    }

    public function faireCommuniquer() {
       $this->_animal->communiquer();
    }
}

$communicateur = new Communicateur(new Belette);
$communicateur->faireCommuniquer();

$communicateur = new Communicateur(new Chameau);
$communicateur->faireCommuniquer();

Une instance de la classe Communicateur a donc en composition un objet du super-type Animal. La seule méthode de cette classe est faireCommuniquer, qui se base sur la méthode communiquer de l’instance d’Animal (et classes filles) en composition. Lorsqu’on appelle faireCommuniquer de Communicateur, on appelle en fait en coulisses Animal->communiquer.

Nous garantissons que quelque soit l’instance de la sous-classe d’Animal passée en paramètre du constructeur de Communicateur, le comportement de Communicateur sera le même…Cela ne vous rappelle pas un certain principe de substitution de Liskov ?

chameau

Source photo : 20 Minutes

PHP : héritage multiple des interfaces

Vous le savez sûrement, les interfaces servent à contraindre des classes à implémenter une ou des méthodes publiques. N’importe quelle classe, abstraite ou concrète, peut les implémenter. Mais saviez-vous que, contrairement aux classes, les interfaces peuvent hériter de plusieurs parents ?

Prenons l’exemple suivant ;


interface Counterializable extends Serializable, Countable {
    public function counterialiser();
}

class CompteEtSerialise implements Counterializable {}

Ici, l’interface Counterializable (contraction de Countable et de Serializable) impose à notre classe concrète CompteEtSerialise l’implémentation de 4 méthodes:
– deux provenant de Serializable (serialize et unserialize)
– une provenant de Countable (count)
– une provenant de Counterializable (counterialiser)

Si l’on exécute ce code, il se produit l’erreur suivante :

PHP Fatal error: Class CompteEtSerialise contains 4 abstract methods
and must therefore be declared abstract or implement the remaining
methods (Counterializable::counterialiser, Serializable::serialize,
Serializable::unserialize, ...)

Ce message d’erreur est bien la preuve que l’héritage multiple, qui ne s’applique pas aux classes, s’applique en revanche aux interfaces !

Les classes abstraites en PHP (partie 3)

abstract class EtreVivant {
    protected $_age;
    protected $_poids;

    public function getAge() {
        echo "J'ai " . $this->_age . " ans", PHP_EOL;
    }

    public function getPoids() {
        echo "Je pèse " . $this->_poids . " grammes", PHP_EOL;
    }

    public function __construct($age = 0, $poids = 0) {
        $this->_age = $age;
        $this->_poids = $poids;
    }

    // rajoutez les setters...
}

interface Respiration {
    public function respirer();
}

interface Reproduction {
    public function seReproduire();
}

abstract class Vegetal extends EtreVivant
                       implements Respiration, Reproduction {
    protected $_chlorophyllien = false;

    public function respirer() {
        echo "Je fais ma photo-synthèse", PHP_EOL;
    }

    public function estChlorophyllien() {
        return (true == $this->_chlorophyllien);
    }
}

class Lys extends Vegetal {
    public function seReproduire() {
        echo "Le vent et les insectes font le travail", PHP_EOL;
    }

    public function __construct($age = 0, $poids = 0) {
        $this->_chlorophyllien = true;
        parent::__construct($age, $poids);
    }
}

$lys = new Lys;
$lys->seReproduire();
$lys->respirer();
$lys->getPoids();
$lys->getAge();

echo ("Je suis ") .
     (!$lys->estChlorophyllien() ? 'pas ' : '') .
     "chlorophyllien", PHP_EOL;

Pour résumer :

  • les classes abstraites ne s’instancient pas, on en hérite donc forcément !
  • ce sont des débuts d’implémentation, elles peuvent comporter des méthodes entièrement ou partiellement écrites
  • une classe abstraite n’a pas forcément de méthode abstraite…
  • …mais une classe qui a une méthode abstraite doit être abstraite
  • une méthode abstraite n’a pas de corps, c’est un prototype : à vous de la concrétiser !
  • une classe abstraite n’est pas tenue de concrétiser les méthodes des interfaces qu’elle implémente; ses filles oui, par contre !
  • mettre une méthode abstraite en mode d’accès privé n’a pas de sens
  • une classe abstraite peut très bien avoir des variables d’instance

 PS : j’ai remarqué que certains élèves étaient choqués de voir un constructeur dans une classe abstraite (« Pourquoi y a-t-il un constructeur si on ne peut PAS les instancier ??? »). Ce constructeur a tout à fait sa place, il peut être utilisé dans les classes filles ou bien être complété dans celles-ci par des actions spécifiques (et invoqué via l’appel statique parent, comme fait ici). 

Les classes abstraites en PHP (partie 2)

Nous avons vu précédemment que les classes abstraites étaient un bon moyen de forcer des implémentations dans les branches et les feuilles de l’arbre d’héritage et que c’était aussi un moyen d’interdire l’instanciation de classes que l’on juge trop « génériques » (on s’instancie pas un « être vivant », c’est une notion trop abstraite, on aimerait plutôt instancier Homme ou Baleine).

Une classe abstraite peut implémenter une (ou des) interfaces :

interface Reproduction {
    public function seReproduire();
}

interface Alimentation {
    public function seNourrir();
}

EtreVivant est une classe abstraite donc rien ne l’oblige à implémenter l’intégralité des fonctions (publiques, toujours !) imposées par les interfaces Reproduction et Alimentation, ce qui serait évidemment le cas si elle était concrète. Comme dans la partie 1, elle délègue cette obligation à la première classe concrète qui va la dériver…

interface Reproduction {
    public function seReproduire();
}

interface Alimentation {
    public function seNourrir();
}

abstract class EtreVivant implements Reproduction, Alimentation {}

class Baleine extends EtreVivant {

    public function seReproduire() {
        // décrivez ici de manière poétique
        // l'accouplement de ces majestueux animaux
    }
    public function seNourrir() {
        echo "Je mange du krill et du zooplancton", PHP_EOL;
    }
}

Bien entendu, une classe abstraite peut prendre en charge une partie de l’implémentation des méthodes imposées par une interface et déléguer le reste à ses filles…

interface Propulsion {
    public function pedaler();
}

interface Freinage {
    public function freiner();
}

abstract class Velo implements Propulsion, Freinage {
    public function pedaler() {
        $this->mettrePiedsSurPedales();
        $this->appuyerSurPedales();
    }

    public function mettrePiedsSurPedales() {
        echo "Je mets les pieds sur les pédales", PHP_EOL;
    }

    public function appuyerSurPedales() {
       echo "J'appuie sur les pédales", PHP_EOL;
    }
}

class BMX extends Velo {
    public function freiner() {
        echo "Les patins appuient sur les roues", PHP_EOL;
    }
}

class VTTDernierCri extends Velo {
    public function freiner() {
        echo "Les plaquettes appuient sur les disques", PHP_EOL;
    }
}

Dans notre cas, Velo, qui est abstraite, a pris en charge une partie des méthodes imposées par les interfaces(imposées…pas vraiment, puisqu’elle est abstraite); elle a choisi d’implémenter pedaler, qui provient de l’interface Propulsion. En effet, qu’on conduise un vélo de course, de descente, un BMX ou le tricycle d’un enfant, on n’y coupe pas : il faut pédaler, et toujours de la même manière !

Par contre le freinage peut être délégué aux filles car son implémentation va dépendre directement de la classe concrète…En effet un vieux vélo ne freine pas forcément de la même manière qu’un nouveau (la plupart des nouveaux vélos ont des freins à disques, les anciens – le mien 🙁 – ont encore des freins à étrier avec des patins…).

Les classes abstraites en PHP (partie 1)

Une classe abstraite est un mécanisme qui comme son nom l’indique permet d’abstraire (i.e, de rendre abstrait) une notion de votre domaine d’application. Quoi de plus générique par exemple que :

abstract class EtreVivant {
    protected $_age;
    protected $_poids;
}

Une classe abstraite peut très bien ne contenir aucune méthode abstraite ! Par contre, il suffit qu’une méthode dans une classe soit abstraite pour que la classe doive l’être à son tour !

abstract class EtreVivant {
    protected $_age;
    protected $_poids;

    abstract public function seReproduire();
}

Tous les êtres vivants savent se reproduire, qu’on  parle d’une bactérie ou d’une baleine megaptera ! Par contre, ils ne se reproduisent pas tous de la même manière…En signalant cette méthode comme étant abstraite, nous laissons donc le soin aux classes qui vont spécialiser EtreVivant d’implémenter (de concrétiser, pour employer les mêmes termes) cette méthode seReproduire.

class Requin extends EtreVivant {
    public function seReproduire() {
        echo "Je m'accouple abdomen contre abdomen", PHP_EOL;
    }
}

class Homme extends EtreVivant {
    public function seReproduire() {
        // Allons, un peu de tenue, tout de même !
    }
}

Une classe abstraite ne s’instancie pas, c’est la règle, et pas qu’en PHP ! Il vous faudra donc la dériver en une classe concrète avant de pouvoir bénéficier de ses fonctionnalités par le mécanisme d’héritage. C’est précisément ce que nous venons de faire !

Une classe abstraite peut hériter d’une classe, elle-même abstraite !

abstract class EtreVivant {
    protected $_age;
    protected $_poids;

    abstract public function seReproduire();
}

abstract class Vegetal extends EtreVivant {

}

Ici, nul besoin d’implémenter la méthode seReproduire qui est pourtant imposée par EtreVivant ! Pourquoi ? Parce que je suis dans une classe abstraite et que je n’en ai donc pas l’obligation ! Par contre, la première classe concrète dans la hiérarchie d’héritage (la première fille de Vegetal, pour parler concrètement) DEVRA obligatoirement implémenter cette méthode sous peine de déclencher une erreur fatale…


abstract class EtreVivant {
protected $_age;
protected $_poids;

abstract public function seReproduire();
}

abstract class Vegetal extends EtreVivant {}

class Rododendron extends Vegetal {}

Hélas… »PHP Fatal error:  Class Rododendron contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (EtreVivant::seReproduire) ».