Archives mensuelles : avril 2013

Les principes SOLID expliquées à ma fille

Ma fille ne comprend rien à l’informatique. Il faut dire qu’à 6 ans, on a autre chose à faire que de se passionner pour ces choses bizarres ! Et puis l’informatique ça a l’air compliqué ! On voit souvent Papa se prendre la tête à deux mains en se demandant pourquoi c’est fait de manière si compliquée alors que le problème adressé est on ne peut plus simple. J’ai tout de même essayé de m’imaginer en train d’expliquer à ma petite tête brune à couettes les principes SOLID, énoncés par « Uncle Bob » il y a maintenant plus d’une décennie.

Commençons par le commencement : le S de SOLID, c’est pour Single Responsibility (responsabilité unique) : une classe n’est pas comme ces équipiers qu’on voit au comptoir des fast-foods; elle ne doit pas faire le drive-in, servir les boissons, nettoyer les tables, faire chauffer les frites, non : UNE SEULE chose à la fois.

Le O c’est pour Open/Closed (ouvert/fermé) : une classe doit être fermée à la modification (de son code source) mais ouverte à l’extension : on n’essaie pas de faire rentrer un carré dans un rond, on crée une « forme », qu’on spécialisera ensuite en rond, carré, losange, triangle, etc.

Le L rend hommage à Madame Barbara Liskov et son principe de substitution: mon objet chimpanzé de la classe Singe a une méthode manger(Fruit) : la banane, la mangue et la noix de coco sont des fruits; ma méthode manger doit avoir le même comportement que je fasse manger(Banane), manger(Mangue) ou manger(Coco), c’est à dire si je substitue le sous-type Banane, Mangue ou Coco au super-type Fruit.

Le I évoque la ségrégation des interfaces (Interface Segregation) : certains animaux savent voler, le singe est un animal mais ne sait pas voler. Pourquoi le faire dépendre d’une classe Animal qui implémenterait une interface AnimalVolant si on sait qu’il ne volera jamais ? Ce principe est très simple : une classe ne doit pas dépendre d’interfaces dont elle n’a pas l’utilité ! L’objectif est ici, une fois de plus, de réduire le couplage inutile entre les classes.

Le dernier de ces 5 principes est le principe d’inversion des dépendances (D pour Dependency Inversion). A est client de B, lui-même client de C; on peut dire que A dépend de B qui dépend de C. Prenons A et B: ce n’est plus A qui va dépendre de B, mais A qui va dire à B ce dont il a besoin. B ne dit plus à A « voilà comment je fonctionne, débrouille toi ! », c’est A qui dit à B « voilà ce dont j’ai besoin, donne le moi ! »…Nous avons donc bien inversé le rapport de force entre fournisseur et client !

Allez, je vous laisse, j’ai un match entre les Littlest Pet Shop et les My little Pony à gérer…

IMG_3521

Sources :

Pour ceux que l’anglais ne rebute pas, je vous conseille l’excellent site d’Oncle Bob et ses hilarants (autant qu’instructifs) codecastsCleanCoders

Toujours dans la langue de Shakespeare…

http://aspiringcraftsman.com/2008/12/28/examining-dependency-inversion/

MySQL : poser un index qui…pénalise le temps d’exécution !

Avant de poser un index sur un (ou plusieurs) champs d’une table, il faut évaluer de la manière la plus précise possible l’impact qu’aura cette pose sur le temps d’exécution des requêtes. Lorsque les développeurs découvrent les index (j’en connais qui sont en poste depuis des années et ne savent même pas ce que c’est…), ils ont tendance à en mettre de partout. Gare ! Un index posé à tort peut pénaliser les sélections, les mises à jour/suppressions/insertions. Regardons un exemple très simple; Henri a décidé que puisque les index sont là pour accélérer les recherches, il va en poser un sur sa table, dont voici le schéma (l’intention) :


create table if not exists test_index
(
   id mediumint unsigned not null primary key,
   nom char(4) not null,
   valid tinyint unsigned not null
);

Sa table comporte 100 000 tuples, qu’il insère avec une procédure stockée :

DROP PROCEDURE IF EXISTS insertion;

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

    WHILE (i<=100000) DO
        INSERT INTO test_index VALUES(i,'test', 1);
        SET i=i+1;
    END WHILE;
END
//
CALL insertion();

Il part du principe (plutôt intelligent, au demeurant) que la requête qu’il fait le plus souvent étant :


select * from test_index where valid = 1;

Il serait judicieux qu’il pose un index sur valid :


alter table test_index add index(valid);

Voilà la pose réalisée ! Une fois l’index en place, il a quand même le réflexe de valider que la pose de cet index accélère bien les recherches, comme il le souhaitait…

Il a bien ses 100 000 enregistrements…


mysql> select count(*) from test_index;
+----------+
| count(*) |
+----------+
| 100000 |
+----------+

Avant la pose de l’index, un EXPLAIN sur sa requête favorite donnait :

snapshot2

On voit au passage qu’il réalisait un full table scan (type = ALL, possible_keys = NULL), c’est à dire qu’il parcourait l’ensemble des tuples de sa table (rows). Dorénavant, cela donne :

snapshot2

Henri constate avec satisfaction que la pose de son index lui fait lire moitié moins d’enregistrements (regardez la colonne rows). Pourtant, lorsqu’il lance sa requête, Henri est très déçu :


100000 rows in set (0.19 sec)

Alors que lorsqu’il détruit l’index, il obtient :


100000 rows in set (0.10 sec)

Pour résumer, Henri lit moitié moins de tuples…en deux fois plus de temps ! Ce n’est pas du tout conforme à l’idée qu’il se faisait des index et ses espoirs sont déçus ! En posant un index inutile sur un champ qui n’a qu’une valeur possible (1), Henri a posé un index qui pénalise le temps d’exécution de sa requête…Il aura tout de même compris que le seul vrai moyen de vérifier qu’on tire des bénéfices d’un index est le benchmarking des requêtes qui l’utilisent.

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 !

MySQL : l’opérateur ensembliste UNION (2/2)

Revenons sur l’opérateur ensembliste UNION et voyons comment il traite les duplicatas :

create table employe (
    id tinyint unsigned NOT NULL PRIMARY KEY,
    nom varchar(20) NOT NULL,
    prenom varchar(30) NOT NULL
);

create table manager(
    id smallint unsigned NOT NULL PRIMARY KEY,
    nom varchar(30),
    prenom varchar(20)
);

INSERT INTO employe VALUES (1, 'Klein', 'Roger'),
                           (2, 'Bagnole', 'Marcel'),
                           (3, 'De Narvale', 'Nadine');

INSERT INTO manager VALUES (3, 'Avignon', 'Eric'),
                           (2, 'Avril', 'Mathilda'),
                           (1, 'Klein', 'Roger');

Faisons l’union de ces deux relations :


select * from employe union select * from manager;

Roger Klein n’apparaît qu’une seule fois : UNION supprime bien les doublons !

Pour avoir l’intégralité de l’union des deux extensions de ces relations, il suffit de faire :


select * from employe union ALL select * from manager;

Et voilà que nous avons tout à présent !

snapshot2

 

Utiliser un UNION ALL vous permettra de faire apparaître l’intégralité des extensions des schémas unis.

MySQL : l’opérateur ensembliste UNION (1/2)

En algèbre relationnelle, UNION est un opérateur dit « ensembliste ». Par définition, il travaille sur au moins deux ensembles (l’extension de deux relations), qu’il agrège tout en éliminant les éventuels doublons. Créons deux relations matérialisant des employés et des managers au sein d’une entreprise pour voir comment tout cela fonctionne :


create table employe (
    id tinyint unsigned NOT NULL PRIMARY KEY,
    nom varchar(20) NOT NULL,
    prenom varchar(30) NOT NULL
);

create table manager(
    id smallint unsigned NOT NULL PRIMARY KEY,
    nom varchar(30),
    prenom varchar(20)
);

INSERT INTO employe VALUES (1, 'Klein', 'Roger'),
                           (2, 'Bagnole', 'Marcel'),
                           (3, 'De Narvale', 'Nadine');

INSERT INTO manager VALUES (1, 'Avignon', 'Eric'),
                           (2, 'Avril', 'Mathilda'),
                           (3, 'Klein', 'Roger');

Ces deux relations ont le même schéma, c’est à dire le même nombre d’attributs; c’est une des conditions de l’UNION. Lorsque l’on fait l’union de nos deux relations, par la requête :


select * from employe union select * from manager;

On obtient les tuples suivants :

snapshot2

Vous notez que Roger Klein est présent deux fois, car il est à la fois employé et manager; ce n’est pas un doublon au sens strict du terme car il a deux valeurs d’identifiant différentes dans chaque table. Il apparaît donc logiquement deux fois, ce qui n’aurait pas été le cas s’il avait eu la valeur d’id 3 dans employe ou de 1 dans manager.

La condition pour réaliser une UNION, je l’ai déjà dit plus haut, est d’avoir le même schéma dans les deux tables, c’est à dire le même nombre d’attributs; prenons deux tables qui n’ont aucun rapport entre elles :


create table chevre (

    id tinyint unsigned NOT NULL PRIMARY KEY,

    nom varchar(20) NOT NULL,

    age tinyint unsigned NOT NULL default 0

);

create table chou (

    variete varchar(15) NOT NULL PRIMARY KEY,

    poids tinyint unsigned NOT NULL default 0,

    couleur varchar(10) NOT NULL

);

insert into chevre values (1, 'Biquette', 10),
                          (2, 'Lola', 13);

insert into chou values ('romanesco', 2, 'vert'),
                        ('chinois', 1, 'blanc');

Faisons l’UNION de ces deux relations de même schéma :


select * from chevre UNION select * from chou;

Je ne comprends pas, on me répète depuis tout petit qu’il ne faut pas ménager la chèvre et le chou et pourtant, stupeur…

snapshot2

C’est tout à fait possible d’avoir les deux ! Vous avez vu que mes champs sont mélangés, il ne sont pas de même type (le premier champ de chevre est un entier non signé alors que le premier champ de chou est une chaîne de caractères, le deuxième est une chaîne de caractères dans l’un et un entier dans l’autre, etc.). Au final, le résultat de notre union ne veut pas dire grand chose sémantiquement parlant, regardez le nom des colonnes : nom contient le nom de nos sympathiques caprins mais aussi le poids de nos choux, age contient l’âge de nos ruminants mais aussi la couleur de nos choux.

Maman avait donc raison : on ne doit pas ménager la chèvre et le chou !

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) ».

Pour en finir avec le display length sur les types de données numériques de MySQL

Contrairement à la croyance populaire, écrire :


create table eleve (

id_eleve tinyint(2) unsigned NOT NULL PRIMARY KEY

) 

ne fera pas en sorte que vous vous retrouviez avec des nombres inférieurs à 99. Ce 2 entre parenthèses n’est utile que lorsque vous utilisez l’option ZEROFILL (littéralement « remplir avec des zéros »). La preuve, insérez donc 255, qui est la limite haute d’un tinyint non signé :

insert into eleve values (255);

Si vous faites :

select * from eleve;

Vous verrez bien 255 s’afficher ! Si par contre vous utilisez l’option ZEROFILL, comme suit :

create table eleve (
id_eleve tinyint(2) unsigned ZEROFILL NOT NULL PRIMARY KEY
)

Alors en insérant 1 :

insert into eleve values (1);

Vous verrez, après un nouveau select, que ce chiffre a été complété (sur la gauche, évidemment…) par autant de zéros qu’il faut pour satisfaire votre longueur d’affichage (display length), qui est de 2 !