Archives mensuelles : avril 2013

MongoDB : une présentation en 10 slides

J’ai récemment déterré un petit diaporama créé pour une présentation d’une demi-heure de MongoDB aux développeurs du site e-commerce Allopneus (avec l’aide de mon compère Lucas Filippi).

Cette présentation a été rédigée à la suite de notre venue à la conférence MongoDB en juin 2012 sur Paris.

Le document en PDFTECH-TALK-001

Le site de Lucas Filippi : LUFI

Installer phpUnit avec Composer

Téléchargez d’abord Composer. Placez vous dans le répertoire de votre choix (vérifiez naturellement que vous y avez les droits d’écriture) et tapez :

curl -sS https://getcomposer.org/installer | php

ensuite vous installez phpUnit à proprement parler :

php composer.phar require "phpunit/phpunit"
Please provide a version constraint for the phpunit/phpunit requirement: 3.7.*     
composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing symfony/yaml (v2.2.1)
    Downloading: 100%         

  - Installing phpunit/php-text-template (1.1.4)
    Downloading: 100%         

  - Installing phpunit/phpunit-mock-objects (1.2.3)
    Downloading: 100%         

  - Installing phpunit/php-timer (1.0.4)
    Downloading: 100%         

  - Installing phpunit/php-token-stream (1.1.5)
    Downloading: 100%         

  - Installing phpunit/php-file-iterator (1.3.3)
    Downloading: 100%         

  - Installing phpunit/php-code-coverage (1.2.9)
    Downloading: 100%         

  - Installing phpunit/phpunit (3.7.19)
    Downloading: 100%         

phpunit/php-code-coverage suggests installing ext-xdebug (>=2.0.5)
phpunit/phpunit suggests installing phpunit/php-invoker (>=1.1.0,<1.2.0)

Vérifiez la bonne tenue de votre fichier composer.json :

cat composer.json 
{
    "require": {
        "phpunit/phpunit": "3.7.*"
    }
}

Vous trouverez l’exécutable phpunit dans le sous-répertoire « vendor/bin ». Lancez le et vous verrez la traditionnelle liste des options s’afficher à l’écran !

Bons tests unitaires !

PHP objet : le late static binding en 5 minutes chrono

913_always_late_car_decal__04163

Top, c’est parti ! Tout d’abord, qu’est-ce que le late static binding (résolution statique à la volée), me direz-vous ? C’est un mécanisme qui, dans un contexte d’appel statique, permet de faire référence à la classe courante (la « vraie » classe). Voici comment nous faisions (avant PHP 5.3) dans un contexte d’héritage statique (A a une méthode statique dont B hérite tout en l’écrasant – même si elle est identique à l’originale) :


class A {
    public function pointDentree() {
        self::methode();
    }

    public static function methode() {
        echo __CLASS__, PHP_EOL;
    }
}

class B extends A {
    public static function methode() {
        echo __CLASS__, PHP_EOL;
    }
}

$b = new B;
$b->pointDentree();

Nous instancions un objet de la classe B, nous nous attendons donc à avoir « B » affiché à l’écran or c’est A qui s’affiche, signe que la résolution statique ne se fait pas au runtime (exécution) mais à la définition de la classe (compilation). Pour obtenir le comportement attendu, il suffit de faire usage du mot clé static :


class A {
    public function pointDentree() {
        static::methode();
    }

    public static function methode() {
        echo __CLASS__, PHP_EOL;
    }
}

class B extends A {
    public static function methode() {
        echo __CLASS__, PHP_EOL;
    }
}

$b = new B;
$b->pointDentree();

Ici j’ai bien B qui s’affiche ! PHP nous donne les raisons de la limitation du mot clé self :

Les références statiques à la classe courante, avec self:: ou __CLASS__, sont résolues en utilisant la classe à laquelle appartiennent les fonctions, celle où elles ont été définies (source : http://php.net/manual/fr/language.oop5.late-static-bindings.php)

Utilisez donc à bon escient ce mécanisme de late static binding dans vos développements, un effet de bord est si vite arrivé quand on fait de l’héritage statique !

MySQL : différence entre = et LIKE dans un SELECT

like a boss !

LIKE…a bo$$ !

On entend souvent dire qu’utiliser l’opérateur arithmétique = ou faire un LIKE sur une chaîne de caractères dans MySQL revient au même. Oui et non…Oui car au final les tuples qui « remontent » de nos tables sont les mêmes mais non car en coulisses l’impact sur le temps d’exécution n’est pas forcément le même.
Voyons sans plus tarder un exemple avec une table composée de 2 champs; un champ char(6) qui occupe donc 6 octets puisque les chaînes générées le sont avec des caractères ASCII et un champ varchar(6) qui va occuper 7 octets – un préfixe d’un octet + nos 6 octets de caractères – dans laquelle nous insérons des chaînes de caractères aléatoires et en nombre assez conséquent (100 000 entrées). Ne vous inquiétez pas si cela prend quelques secondes à s’exécuter…

CREATE TABLE test_like (
    cha char(6) NOT NULL,
    var varchar(6) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- le début de notre proc' stock'

DROP PROCEDURE IF EXISTS insertion;

DELIMITER //
CREATE PROCEDURE insertion()
BEGIN
    DECLARE i INT DEFAULT 1;

    WHILE (i<=100000) DO
        INSERT INTO test_like VALUES
        (
        SUBSTRING(MD5(RAND()) FROM 1 FOR 6),
        SUBSTRING(MD5(RAND()) FROM 1 FOR 6));
        SET i=i+1;
    END WHILE;
END
//
CALL insertion();

C’est vrai que de prime abord faire

 select * from test_like
where cha like 'ee43b9' 

ou

 select * from test_like
where cha = 'ee43b9' 

sur notre champ de type CHAR revient au même, tout comme :

select * from test_like
where var like 'fb7bb8'

produit le même résultat que

select * from test_like
where var = 'fb7bb8'

J’ai lu sur certains forums anglophones que le comportement de = ou like dépendait du type de données de la colonne (CHAR/VARCHAR), c’est peut-être vrai sur certains RDBMS, mais pas avec MySQL, vous en avez la preuve concrète. Le changement est surtout visible lorsque l’on pose un index sur la colonne concernée :

ALTER TABLE test_like ADD INDEX (cha)

Ainsi, voici ce que nous donne un EXPLAIN. Tout d’abord sur le LIKE :

explain select * from test_like where cha like 'ee43b9'

+—-+————-+———–+——-+—————+——+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+———–+——-+—————+——+———+——+——+————-+
| 1 | SIMPLE | test_like | range | cha | cha | 18 | NULL | 1 | Using where |
+—-+————-+———–+——-+—————+——+———+——+——+————-+
1 row in set (0.01 sec)

Et ensuite sur le = :

explain select * from test_like where cha = 'ee43b9'

+—-+————-+———–+——+—————+——+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+———–+——+—————+——+———+——-+——+————-+
| 1 | SIMPLE | test_like | ref | cha | cha | 18 | const | 1 | Using where |
+—-+————-+———–+——+—————+——+———+——-+——+————-+
1 row in set (0.01 sec)

Nous obtenons la même chose sur le champ de type VARCHAR après avoir posé un index dessus. Une recherche avec un LIKE sur un champ indexé donne un type = range alors qu’une recherche sur un champ indexé donne avec une égalité stricte un type = ref. Voyons ce que dit la documentation officielle à ce sujet :

ref peut être utilisé pour les colonnes indexées, qui sont comparées avec l’opérateur =.

C’est bien le cas ! Pour le range :

range peut être utilisé lorsqu’une colonne indexée est comparée avec une constante comme =<>>>=<<=,IS NULL<=>BETWEEN ou IN.

Les exemples de la documentation officielle montrent bien que range peut-être AUSSI utilisé avec un = mais plutôt sur des types de données numériques…

Les tests réalisés sur un serveur MySQL situé sur le réseau local

Pour le champ de type CHAR

CHAR(6) indexé, LIKE sur une valeur existante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE cha LIKE 'edd34e'
1 total, Traitement en 0.0103 sec.

CHAR(6) indexé, LIKE sur une valeur inexistante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE cha LIKE 'fb7bb8'
MySQL n'a retourné aucune ligne. ( Traitement en 0.0007 sec. )

CHAR(6) indexé, = sur une valeur existante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE cha =  'edd34e'
Traitement en 0.0008 sec.

CHAR(6) indexé, = sur une valeur inexistante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE cha =  'udd34e'
MySQL n'a retourné aucune ligne. ( Traitement en 0.0006 sec. )

Pour le champ de type VARCHAR

VARCHAR(6) indexé, LIKE sur une valeur existante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE var LIKE  '64eaee'
1 total, Traitement en 0.0455 sec.

VARCHAR(6) indexé, LIKE sur une valeur inexistante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE var LIKE  '88eaee'
MySQL n'a retourné aucune ligne. ( Traitement en 0.0659 sec. )

VARCHAR(6) indexé, = sur une valeur existante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE var =  '6753ec'
1 total, Traitement en 0.0520 sec.

VARCHAR(6) indexé, = sur une valeur inexistante, bypass du cache de requêtes MySQL :

SELECT SQL_NO_CACHE *
     FROM test_like
     WHERE var =  '8888ec'
MySQL n'a retourné aucune ligne. ( Traitement en 0.0513 sec. )

D’après ce petit benchmark très rapide et sans grande prétention :

  • sur un VARCHAR, like est un peu plus rapide que =
  • sur un CHAR intégralement rempli, = est beaucoup plus rapide que like

PHP : le registre d’autoload de la SPL

Pour bénéficier de l’autoloading (ou chargement automatique), on peut utiliser la fonction built-in __autoload ou bien la pile (ou registre) d’autoload. Je privilégie davantage la seconde, parce qu’une classe est pour moi une unité de programme plus structurée (et plus structurante !) qu’une simple fonction jetée à la hâte dans le code. Voici un exemple très simple d’utilisation de la pile de chargement automatique des classes.

class Autoload
{
    private static $_classDir = './classes/';

    public static function classesAutoloader($class)
    {
        $path = static::$_classDir . "$class.php";

        if (file_exists($path) && is_readable($path)) {
            require $path;
        }
    }
}

spl_autoload_register('autoload::classesAutoloader');
// hop !, la méthode statique est placée dans la pile !

Ici nous définissons une classe avec une fonction statique chargée de donner à PHP les moyens d’aller trouver les fichiers contenant les définitions de classes. Le nom de la classe à trouver est injecté par le langage dans les fonctions de chargement automatique: c’est cette variable $class qui va nous servir à construire un chemin vers le fichier qui contient la définition de la classe.

Une fois notre méthode implémentée, nous la plaçons dans la pile d’autoloading grâce à un appel à spl_autoload_register (notez bien l’insensibilité à la casse) et le tour est joué. Evidemment, vous empilerez autant de méthodes que souhaité même si cet exemple, qui se veut simplissime, n’en contient qu’une (attention toutefois à la surenchère…).

L’auto-loading est devenue une pratique courante depuis la sortie de PHP 5 et plus encore depuis l’avènement des frameworks MVC en PHP, il est indispensable en 2013 de comprendre en quoi consiste ce mécanisme !

Pour plus d’info : la documentation PHP

PHP : un exemple simple du design pattern Template Method

Vous cherchiez un design pattern facile à aborder ? Le design pattern template method est celui qu’il vous faut ! Son principe est très simple : dans une classe, une méthode dite template est composée de sous-méthodes dont on sait que chaque sous-classe l’implémentera à sa manière. Ces sous-méthodes sont généralement en type d’accès protégé car invoquées uniquement par cette fameuse méthode template; l’extérieur n’a pas à connaître les mystères de votre implémentation (encapsulation, vous dîtes ?). Bien entendu, étant donné que chaque classe fille implémentera ces méthodes comme bon lui semble, il convient de les signifier comme abstraites dans la classe mère.

Imaginons une classe TunnelCommande qui expose une méthode template nommée finaliserCommande; cette méthode décrit un algorithme en spécifiant ce qui devra être fait par ses sous-classes et dans quel ordre. Cette classe comporte la méthode payePort dite « adaptateur » (hook) qui peut être réécrite dans les classes filles. Elle sert à conditionner une partie du flot d’exécution de l’algorithme de la méthode template. Son utilité dans notre cas est de permettre à un certain type d’utilisateur de s’affranchir du paiement des frais de port.

abstract class TunnelCommande
{
    public function finaliserCommande(): void
    {
        $this->faireTotal();
        
        if ($this->payePort()) {
            $this->ajouterFraisPort();
        }

        $this->rediriger('page_paiement');
    }

    public function payePort(): bool
    {
    	return true;
    }

    public function rediriger(string $template): void
    {
    	echo "Redirection vers ", $template, PHP_EOL;
    }

    abstract protected function faireTotal(): void;
    abstract protected function ajouterFraisPort(): void;
}

Nous avons deux classes concrètes qui dérivent TunnelCommande et implémentent les méthodes abstraites en leur donnant un comportement spécifique. Dans CommandePremium, la méthode ajouterFraisPort qui est imposée par la classe mère abstraite ne fait rien, payePort renvoyant false elle ne sera de toutes façons jamais invoquée dans ce scénario là.


class CommandeClient extends TunnelCommande
{
    protected function faireTotal(): void
    {
        echo "Je fais le total", PHP_EOL;
    }

    protected function ajouterFraisPort(): void 
    {
        echo "J'applique les frais de port du client normal", PHP_EOL;
    }
}

class CommandePremium extends TunnelCommande
{
    protected function faireTotal(): void
    {
        echo "Appliquer 5% de rabais pour les clients Premium", PHP_EOL;
    }

    protected function ajouterFraisPort(): void
    {
        return;
    }

    public function payePort(): bool
    {
    	return false;
    }
}

$premium = new CommandePremium;
$premium->finaliserCommande();

$standard = new CommandeClient;
$standard->finaliserCommande();

Dans ce design pattern, tout le travail est fait dans la classe mère, abstraite. Quand je dis « tout le travail », je parle de la structure générale de l’algorithme, de l’ordre des opérations. Evidemment, la responsabilité de l’implémentation des détails de cet algorithme « général » est déléguée aux classes dérivées, via le mécanisme d’abstraction.

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

MySQL : comparaison rapide des types de données CHAR et VARCHAR

Quand on choisit d’utiliser des champs de type chaîne de caractères dans une table MySQL (mais pas que…), on en vient rapidement à se poser la question suivante :

CHAR ou VARCHAR ?

Voici le tableau comparatif que nous donne la documentation MySQL. Il suppose que nous utilisions un jeu de caractères codés sur 1 octet comme latin1 par exemple (de son vrai nom ISO 8859-1) :

snapshot2

CHAR va de 0 à 255 caractères. La longueur d’un champ de type CHAR est fixée à la valeur déclarée lors de la définition du champ : si vous créez un champ de type CHAR(30) et que vous souhaitez y insérer une chaîne de 31 caractères, cette valeur sera stockée sous une forme tronquée. Si la valeur insérée est inférieure à 30, le « reste » (les caractères manquants pour arriver à 30) sera comblé avec des espaces. Lorsque la valeur sera récupérée, les espaces seront enlevés automatiquement, vous n’y verrez que du feu !

Les VARCHAR sont eux utilisés pour des chaînes de longueur variable et donc appropriés pour des données dont on ne peut prédire la longueur de façon certaine. VARCHAR peut stocker jusqu’à 65535 caractères (bien plus que les 255 qu’une grande partie des gens ont en tête). Leur taille étant variable, elle doit être stockée quelque part…Ainsi, pour tout type VARCHAR, MySQL réserve un préfixe d’un octet si la taille des données est inférieure ou égale à 255 (un octet = 8 bits et 28 = 256) et deux octets dans le cas contraire (2 octets = 16 bits, 216 = 65536).

Que lit-on sur ce tableau ? Lorsque l’on stocke une chaîne de caractères vide dans un champ en CHAR(4), quatre caractères « espace » sont réservés et donc 4 octets alloués ; avec un VARCHAR(4), on n’alloue que l’octet nécessaire au préfixe des chaînes de moins de 255 caractères. Si l’on stocke deux caractères, en CHAR(4), deux espaces sont alloués « pour rien » alors qu’en VARCHAR(4) on a toujours l’octet nécessité par le préfixe et les deux octets de chaque caractère. Jusqu’ici, VARCHAR est moins gourmand en espace disque. La tendance s’inverse lorsque l’on remplit CHAR avec le nombre exact de caractères attendus : on économise un octet par rapport à un VARCHAR ! Lorsque la chaîne dépasse la longueur maximale prévue, elle est tronquée dans les deux cas, mais c’est toujours CHAR qui est plus économique !

Conclusion, quand on sait qu’une chaîne de caractères aura une longueur définie, mieux vaut privilégier CHAR (si cette longueur est évidemment inférieure à 255, mais ce sera dans 99,99% des cas, n’est-ce pas ?).