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 !