DEV Community

Cover image for Aide mémoire Programmation Orientée Objet
Abdelkarim AIN
Abdelkarim AIN

Posted on • Edited on

Aide mémoire Programmation Orientée Objet

Cet article présente la syntaxe objet avec des exemples PHP, Java & Kotlin

Introduction

La POO c'est 4 concepts

  • Concept de modélisation à travers la notion de classe et d’instanciation de ces classes.
  • Concept d’action à travers la notion d’envoi de messages et de méthodes à l’intérieur des objets.
  • Concept de construction en créant un objet en reposant sur la définition d’une classe.
  • Concept d'encapsulation l'accès aux propriétés se fait via un getter et un setter. (inaccessible de l'exterieur).

Mais surtout
Ça permet de représenter informatiquement quelque chose du monde réel.

Deux notions

  • Les classes
  • Les objets

Définition

La programmation orientée objet (POO) est un paradigme de programmation qui utilise des objets pour représenter des entités du monde réel. Les objets sont des instances de classes, qui peuvent contenir des données et des méthodes. Les classes sont des modèles pour créer des objets.

Avantages

  • Réutilisation : on peut réutiliser des classes déjà existantes.
  • Abstraction : on peut cacher les détails d'implémentation.
  • Encapsulation : on peut cacher les données et les méthodes.
  • Héritage : on peut créer des classes à partir d'autres classes.

La modélisation

Avant de parler des classes ou des objets, je vous propose de regarder le principe de la modélisation

Les classes

  • Serviront de moule pour créer des objets.
  • Une classe étant une définition, elle nous servira plus tard à créer des objets.

Une classe est composée de :

  • D'attributs (variable d'instance ou propriété).
  • De méthodes (actions / opération au niveau de la classe).

Les attributs sont les valeurs qui feront fonctionner notre objet (exemple nom, prenom, age etc.).

Les méthodes vont permettre d'effectuer des actions dans notre objet (exemple presenter(), bouger(), etc.).

Nous avons donc deux types membres dans la classe

  • Des propriétés (le données de l'objet) avec une visibilité
  • Des méthodes (les actions possibles : accélérer, freiner, etc.) avec une visibilité

La visibilité ?

  • Privée : accessible que dans l'objet.
  • Public : accessible hors de l'objet.
  • Protected : Accessible aux enfants (héritage), mais pas hors de la classe.

Les méthodes

  • Comme une fonction, mais encapsulé dans la classe.
  • Possède une visibilité.
  • Possède des paramètres.
  • Surcharge: plusieurs méthodes peuvent avoir le même nom et des paramètres différents (type et/ou ordre).

Les types de méthodes

Dans une classe nous avons différents types de méthodes :

  • Le constructeur.
  • Les méthodes d'actions.
  • Les méthodes accesseurs / mutateurs.

Le constructeur est une méthode qui permet d'initialiser un objet. Il est appelé automatiquement lors de la création d'un objet (new). Son rôle est d'initialiser les attributs de l'objet avec des valeurs spécifiques, selon les paramètres fournis lors de la création de l'objet. Il est possible de définir plusieurs constructeurs dans une classe (surcharge). Il permet de garantir l'intégrité des objets et de s'assurer qu'ils sont initialisés de manière cohérente.

Les méthodes d'actions sont des méthodes qui permettent d'effectuer des actions sur l'objet. Exemple : accelerer(), freiner(), tourner(), etc.

Les méthodes accesseurs / mutateurs sont des méthodes qui permettent de lire ou d'écrire les propriétés de l'objet. Exemple : getVitesse(), setVitesse(), etc. Elles sont appelées getter et setter.

Exemple

Exemple modélisation

class Personne
{

    // Attribut
    public $nom;
    public $prenom;
    private $dateNaissance;
    private $salaire;
    public $nbEnfant;


    // Constructeur
    function __construct($nom, $prenom, $dateNaissance, $nbEnfant = 0)
    {
        $this->nom = $nom;
        $this->prenom = $prenom;
        $this->dateNaissance = $dateNaissance;
        $this->nbEnfant = $nbEnfant;
    }

    // Mutateurs
    public function setSalaire($valeur)
    {
        $this->salaire = $valeur;
    }

    // Accesseur
    public function getSalaire()
    {
        return $this->salaire;
    }


    // Méthode
    public function identite(){
        return $this->nom . " " . $this->prenom;
    }

    // Méthode
    public function age()
    {
        // Implémentation
    }

    // Méthode
    public function argentPoche()
    {
        // Implémentation
    }
}
Enter fullscreen mode Exit fullscreen mode
import java.util.Date;

class Personne
{

    // Attribut
    public String nom;
    public String prenom;
    private Date dateNaissance;
    private Integer salaire;
    public Integer nbEnfant;


    // Constructeur
    public Personne(String nom, String prenom, Date dateNaissance, Integer nbEnfant = 0)
    {
        this.nom = nom;
        this.prenom = prenom;
        this.dateNaissance = dateNaissance;
        this.nbEnfant = nbEnfant;
    }

    // Mutateurs
    public void setSalaire(Integer valeur)
    {
        this.salaire = valeur;
    }

    // Accesseur
    public Integer getSalaire()
    {
        return this.salaire;
    }


    // Méthode
    public String identite(){
        return this.nom + " " + this.prenom;
    }

    // Méthode
    public void age()
    {
        // Implémentation
    }

    // Méthode
    public void argentPoche()
    {
        // Implémentation
    }
}
Enter fullscreen mode Exit fullscreen mode
import java.util.*

internal class Personne(var nom: String, var prenom: String, private val dateNaissance: Date, var nbEnfant: Int) {
    var salaire: Int? = null

    // Méthode
    fun identite(): String {
        return "$nom $prenom"
    }

    // Méthode
    fun age() {
        // Implémentation
    }

    // Méthode
    fun argentPoche() {
        // Implémentation
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Personne
{

  // Variable
 public string nom, prenom;
    public int nbEnfant;
    private DateTime dateNaissance;
    private int salaire;

    public Personne(string nom, string prenom, DateTime dateNaissance, int nbEnfant)
    {
        this.nom = nom;
        this.prenom = prenom;
        this.dateNaissance = dateNaissance;
        this.nbEnfant = nbEnfant;
    }

    public void setSalaire(int value)
    {
        this.salaire = value;
    }

    public int getSalaire()
    {
        return this.salaire;
    }

    public string identite()
    {
        return this.nom + " " + this.prenom;
    }

    public int age()
    {
        //Implémentation
    }

    public void argentPoche(int value)
    {
        //Implémentation
    }
}
Enter fullscreen mode Exit fullscreen mode

Ce qu'il faut retenir

  • Les classes sont instanciables (création d'objets, $unPersonne = new Personne(…)).
  • Les propriétés sont les « variables » de l'objet.
  • Les méthodes sont les « actions » de l'objet.
  • Les méthodes et les propriétés ont des visibilités.
  • Les méthodes peuvent être surchargées.

Les objets

Chaque objet représente un objet du monde réel. Exemple : une voiture, une personne, un élément de menu, etc.

exemple :

  • une personne précise
  • une voiture spécifique
  • Un élément de menu.

⚠️ Utilise les classes précédemment définies ⚠️

$personne1 = new Personne("Valentin", "Brosseau", "28/02/1987", 0);
$personne2 = new Personne("John", "Doe", "01/01/1970", 12); 
Enter fullscreen mode Exit fullscreen mode
Personne personne1 = new Personne("Valentin", "Brosseau", "28/02/1987", 0);
Personne personne2 = new Personne("John", "Doe", "01/01/1970", 12);
Enter fullscreen mode Exit fullscreen mode

👀 Créer un objet == Instancier 👀


Définir une classe

class Personne
{
    // Attribut
    private $nom;
    private $prenom;

    // Constructeur
    function __construct($nom, $prenom)
    {
        $this->nom = $nom;
        $this->prenom = $prenom;
    }

    // Méthode
    public function identite(){
        return $this->nom . " " . $this->prenom;
    }

    // Accesseur
    public function getNom()
    {
        return $this->nom;
    }

    // Mutateur
    public function setNom($nom)
    {
        $this->nom = $nom;
    }
}
Enter fullscreen mode Exit fullscreen mode
class Personne {

    // Attribut
    private String nom;
    private String prenom;

    // Constructeur
    public Personne(String nom, String prenom) {
        this.nom = nom;
        this.prenom = prenom;
    }

    // Méthode
    public String identite(){
        return this.nom + " " + this.prenom;
    }

    // Accesseur
    public String getNom()
    {
        return this.nom;
    }

    // Mutateur
    public void setNom(String nom)
    {
        this.nom = nom;
    }

}
Enter fullscreen mode Exit fullscreen mode
class Personne(val nom: String, val prenom: String) {
    // Méthode
    fun identite(): String {
        return "$nom $prenom"
    }

    // Accesseur
    fun getNom(): String {
        return nom
    }

    // Mutateur
    fun setNom(nom: String) {
        this.nom = nom
    }
}
Enter fullscreen mode Exit fullscreen mode
class Personne {
    // Attribut
    private string nom;
    private string prenom;

    // Constructeur
    public Personne(string nom, string prenom) {
        this.nom = nom;
        this.prenom = prenom;
    }

    // Méthode  
    public string identite() {
        return this.nom + " " + this.prenom;
    }

    // Accesseur
    public string getNom() {
        return this.nom;
    }
}
Enter fullscreen mode Exit fullscreen mode

Instanciation (créer un objet)

// Instanciation
$unePersonne = new Personne("Valentin", "Brosseau");
Enter fullscreen mode Exit fullscreen mode
// Instanciation
Personne unPersonne = new Personne("Valentin", "Brosseau");
Enter fullscreen mode Exit fullscreen mode
// Instanciation
unPersonne = Personne("Valentin", "Brosseau");
Enter fullscreen mode Exit fullscreen mode
// Instanciation
Personne unePersonne = new Personne("Valentin", "Brosseau");
Enter fullscreen mode Exit fullscreen mode

Le constructeur

class Personne
{
    // Attribut
    private $nom;
    private $prenom;

    // Constructeur
    function __construct($nom, $prenom)
    {
        $this->nom = $nom;
        $this->prenom = $prenom;
    }
}
Enter fullscreen mode Exit fullscreen mode
class Personne {
    // Attribut
    private final String nom;
    private final String prenom;

    // Constructeur
    public Personne(String nom, String prenom) {
        this.nom = nom;
        this.prenom = prenom;
    }
}
Enter fullscreen mode Exit fullscreen mode
class Personne(val nom: String, val prenom: String) {
}
Enter fullscreen mode Exit fullscreen mode
class Personne {

  // Attribut
  private string nom;
  private string prenom;

  // Le constructeur
  public Personne(string nom, string prenom)
  {
    this.nom = nom;
    this.prenom = prenom;
  }
}
Enter fullscreen mode Exit fullscreen mode

Accès à une méthode

// Instanciation
$unePersonne = new Personne("Valentin", "Brosseau");

// Appel de la méthode
$unPersonne->afficheIdentite(); // Affiche "Valentin Brosseau"
Enter fullscreen mode Exit fullscreen mode
// Instanciation
Personne unePersonne = new Personne("Valentin", "Brosseau");

// Appel de la méthode
unePersonne.afficheIdentite(); // Affiche "Valentin Brosseau"
Enter fullscreen mode Exit fullscreen mode
// Instanciation
unePersonne = Personne("Valentin", "Brosseau");

// Appel de la méthode
unePersonne.afficheIdentite(); // Affiche "Valentin Brosseau"
Enter fullscreen mode Exit fullscreen mode
// Instanciation
Personne unePersonne = new Personne("Valentin", "Brosseau");

// Appel de la méthode
unePersonne.identite(); // Affiche "Valentin Brosseau"
Enter fullscreen mode Exit fullscreen mode

Accès à une propriété // Accesseur et Mutateur

$unePersonne = new Personne("Valentin", "Brosseau");
$unePersonne->getNom(); // Affiche "Valentin"

$unePersonne->setNom("Chouette");
$unePersonne->getNom(); // Affiche "Chouette", la valeur a été modifiée
Enter fullscreen mode Exit fullscreen mode
Personne unPersonne = new Personne("Valentin", "Brosseau");
unePersonne.getNom(); // Affiche "Valentin"

unePersonne.setNom("Chouette");
unePersonne.getNom(); // Affiche "Chouette", la valeur a été modifiée
Enter fullscreen mode Exit fullscreen mode

L'accès aux propriétés ne fonctionnera que si la visibilité (private, public, protected) ne vous y autorise :

Visibilité Accès depuis
private Seulement depuis l'objet en lui-même
public Depuis n'import où (objet, depuis l'objet, ou depuis l'héritage)
protected Comme, private mais non accessible depuis la classe fille en cas d'héritage

Les collections

Représentation UML

Le losange vide
Agrégation
Ce symbole signifie la notion de composition. Dans notre cas, une Entreprise n’est composée de personne.

Permets de regrouper des listes d'objets.

Représentation UML

En modélisation, la flèche signifie un lien entre les deux classes. En l'occurrence, dans le cas des collections, nous aurons :

  • Le nom « de la collection » qui va contenir les objets.
  • Le nombre minimum & maximum.
  • Exemple, 1 étudiant possède plusieurs devoirs.

Fonctionnellement, nous allons donc avoir dans l'étudiant une collection d'objets du type devoirs. Celle-ci sera nommée lesDevoirs. lesDevoirs sera une propriété de la classe Étudiant.

Déclaration

$lesPersonnes = [];
Enter fullscreen mode Exit fullscreen mode
ArrayList<Personne> lesPersonnes = new ArrayList();
Enter fullscreen mode Exit fullscreen mode
List<Personne> lesPersonnes = new List<Personne>();
Enter fullscreen mode Exit fullscreen mode

Utilisation

$lesPersonnes = [];
$unePersonne = new Personne("Doe", "John");

array_push($lesPersonnes, new Personne("Brosseau", "Valentin"));
array_push($lesPersonnes, $unePersonne);

$nombre = sizeof($lesPersonnes); // 2

$unePersonne1 = $lesPersonnes[0]; // Valentin Brosseau
$unePersonne2 = $lesPersonnes[1]; // John Doe

$lesPersonnes = [];
$nombre = sizeof($lesPersonnes); // 0
Enter fullscreen mode Exit fullscreen mode
ArrayList<Personne> lesPersonnes = new ArrayList<>();
Personne carine = new Personne("John", "Doe");

lesPersonnes.add(new Personne("Valentin", "Brosseau"));
lesPersonnes.add(carine);
int count = lesPersonnes.size(); // 2

Personne laPersonne1 = lesPersonnes.get(0); // Valentin;
Personne laPersonne2 = lesPersonnes.get(1); // Carine;

lesPersonnes.clear();
int count2 = lesPersonnes.size(); // 0
Enter fullscreen mode Exit fullscreen mode
val lesPersonnes = ArrayList<Personne>()
val carine = Personne("John", "Doe")

lesPersonnes.add(Personne("Valentin", "Brosseau"))
lesPersonnes.add(carine)
val count = lesPersonnes.size // 2

val laPersonne1 = lesPersonnes[0] // Valentin;
val laPersonne2 = lesPersonnes[1] // Carine;

lesPersonnes.clear()
val count2 = lesPersonnes.size // 0
Enter fullscreen mode Exit fullscreen mode
List<Personne> lesPersonnes = new List<Personne>();
Personne unePersonne = new Personne("Doe", "John");

lesPersonnes.add(new Personne("Valentin", "Brosseau");
lesPersonnes.add(unePersonne);

Personne unePersonne1 = lesPersonnes[0];
Personne unePersonne2 = lesPersonnes[1];

List<Personne> lesPersonnes = new List<Personne>();

int nombre = lesPersonnes.count(); // 0
Enter fullscreen mode Exit fullscreen mode

Parcours de collection

foreach ($lesPersonne as $laPersonne){
    // $laPersonne contient « un pointeur » vers une des personne de la liste
    // À chaque tour de boucle nous avons la personne suivante.
}
Enter fullscreen mode Exit fullscreen mode
// Version moderne
lesPersonnes.forEach(laPersonne -> {
    // laPersonne contient « un pointeur » vers une des personne de la liste
    // À chaque tour de boucle nous avons la personne suivante.
});

// Version « à l'ancienne »
for (Personne laPersonne : lesPersonnes) {
    // laPersonne contient « un pointeur » vers une des personne de la liste
    // À chaque tour de boucle nous avons la personne suivante.
}
Enter fullscreen mode Exit fullscreen mode
// Version moderne
lesPersonnes.forEach { laPersonne -> }

// Version « à l'ancienne »
for (laPersonne in lesPersonnes) {
    // laPersonne contient « un pointeur » vers une des personne de la liste
    // À chaque tour de boucle nous avons la personne suivante.
}
Enter fullscreen mode Exit fullscreen mode
foreach (Personne laPersonne in lesPersonnes){
    // laPersonne contient « un pointeur » vers une des personne de la liste
    // À chaque tour de boucle nous avons la personne suivante.
}
Enter fullscreen mode Exit fullscreen mode

L'héritage

L'héritage permet de généraliser le fonctionnement d'un objet. L'idée est de mettre dans un « objet parent » la logique de plusieurs objets qui fonctionne de la même façon. Exemple

  • Un humain et une baleine partage des propriété et fonctionnement commun. Nous allons donc créer une super classe mammifère, celle-ci contiendra les méthodes et les propriétés communes.
  • Une Voiture et une Moto sont des véhicules. Nous pouvons donc créer une super classe « Véhicule ».

Comment identifier qu'il s'agit d'un héritage ? C'est simple, si vous pouvez dire « est un » alors il s'agit d'un héritage. Exemple :

  • Un humain est un mammifère.
  • Une Voiture est un Véhicule.
  • Mais nous ne pouvons pas dire qu'un Pompier est un Camion. Il n'y a pas d'héritage.

Le mot clé extends permet de définir une classe enfant. Exemple

class Mammifere {
    private $vertebre = true;

    public function print() {
        echo "Je suis un mammifère";
    }

    public function manger(){
        echo "Je mange";
    }
}

// Humain hérite de Mammifere
class Humain extends mammifere {
    private $prenom = "";

    function __construct($prenom)
    {
        parent::__construct();
        this->$prenom = $prenom;
    }

    public function manger(){
        echo "Je suis omnivore";
    }
}

$unHumain = new Humain("Valentin");
$unHumain->print(); // Je suis un mammifère.
$unHumain->manger(); // Je suis omnivore
Enter fullscreen mode Exit fullscreen mode
class Mammifere {
    private Boolean vertebre = true;

    public void print() {
        System.out.println("Je suis un mammifère");
    }

    // Redéfinition de méthode
    public String manger(){
        System.out.println("Je mange");
    }
}

class Humain extends Mammifere {
    private String prenom = "";

    public Hunain(String prenom)
    {
        super();
        this.prenom = prenom;
    }

    // Redéfinition de méthode
    public String manger(){
        System.out.println("Je suis omnivore");
    }
}

Humain unHumain = new Humain("Valentin");
unHumain.print(); // Je suis un mammifère.
unHumain.manger(); // Je suis omnivore.
Enter fullscreen mode Exit fullscreen mode
internal open class Mammifere {
    private val vertebre = true
    fun print() {
        println("Je suis un mammifère")
    }

    // Redéfinition de méthode
    open fun manger() {
        println("Je mange")
    }
}

internal class Humain(val prenom: String) : Mammifere() {
    // Redéfinition de méthode
    override fun manger() {
        println("Je suis omnivore")
    }

    var unHumain = Humain("Valentin")
}

Humain unHumain = Humain("Valentin");
unHumain.print(); // Je suis un mammifère.
unHumain.manger(); // Je suis omnivore.
Enter fullscreen mode Exit fullscreen mode
public class Mammifere {

    private Bool vertebre = true;

    public void print() 
    {
        Console.WriteLine("Je suis un mammifère");
    }

    public void manger()
    {
        Console.WriteLine("Je mange");
    }
}

public class Humain : Mammifere {
    private string prenom = "";

    public Humain(string prenom) : base()
    {
        this.prenom = prenom;
    }

    public string manger()
    {
        Console.WriteLine("Je suis omnivore");
    }
}

Humain unHumain = new Humain("Valentin");
unHumain.string(); // Je suis un mammifère
unHumain.manger(); // Je suis omnivore
Enter fullscreen mode Exit fullscreen mode

Synthèse héritage

  • La classe mère contient la logique partagée.
  • La classe fille contient la logique spécifique.
  • Si nous pouvons dire « est un » alors il s'agit d'un héritage.
  • Un mot-clé Extends class Humain extends Mammifere.
  • Vous devez construire le parent dans le constructeur de l'enfant.
  • Permets de généraliser un objet afin de partager des propriétés communes..
  • mais il est également possible de spécialiser / redéfinir un objet.
    • Redéfinition, comme la surcharge, mais entre la classe fille et la classe mère.
  • Il est possible d'appeler une méthode de la classe mère depuis la classe fille.
    • parent::manger(); // super.manger();
    • Ou d'appeler la méthode la plus proche de la classe fille :
    • $this->manger();

Le polymorphisme

Le polymorphisme peut être vu comme la capacité de choisir dynamiquement la méthode qui correspond au type réel de l’objet. C’est un concept fondamental de la programmation orientée objet.

Le mot polymorphisme vient du grec poly (plusieurs) et morphisme (forme). Il signifie donc « plusieurs formes ». L'héritage est une forme de polymorphisme.

L'encapsulation

Mécanisme consistant à rassembler les données et les méthodes au sein d'une structure en cachant l'implémentation de l'objet, c'est-à-dire en empêchant l'accès aux données par un autre moyen que les services proposés.

L'encapsulation

Sécurité ?
Vous avez ici un élément important, la notion de visibilité et de gestion de l'accès aux propriétés est fondamentale. L'encapsulation fait partie d'une des raisons pourquoi la POO est à favoriser pour réaliser un développement sécurisé.

Les méthodes statiques

Les méthodes statiques sont des méthodes qui peuvent être appelées sans avoir besoin d'instancier un objet. Elles sont déclarées avec le mot-clé static. Les méthodes statiques sont souvent utilisées pour créer des fonctions utilitaires.

// Déclaration
class Personne {
    static function laReponseDeLunivers(){
        return 42;
    }
}

// Utilisation
Personne::laReponseDeLunivers();
Enter fullscreen mode Exit fullscreen mode
// Déclaration
class Personne {
    static int laReponseDeLunivers(){
        return 42;
    }
}

// Utilisation
Personne.laReponseDeLunivers();
Enter fullscreen mode Exit fullscreen mode
// Déclaration
internal object Personne {
    fun laReponseDeLunivers(): Int {
        return 42
    }
}

// Utilisation
Personne.laReponseDeLunivers();
Enter fullscreen mode Exit fullscreen mode
// Déclaration
public class Personne {

    public static int laReponseDeLunivers(){
        return 42;
    }

}

// Utilisation
Personne.laReponseDeLunivers();
Enter fullscreen mode Exit fullscreen mode

Abstraction et Interface

Les classes abstraites

Définition :

  • Une classe abstraite est une classe qui ne peut pas être instanciée.
  • Permets de définir des comportements (méthodes) dont l'implémentation (le code dans la méthode) se fait dans les classes filles.

Ainsi, on a l'assurance que les classes filles respecteront le contrat défini par la classe mère abstraite.

Nous aurons donc deux types de classes :

  • Des classes abstraites (sans code, non instanciable).
  • Des classes concrètes (avec du code, et instanciable).

Une classe abstraite doit posséder au moins une méthode abstraite (c'est-à-dire sans code). Si nécessaire, elle peut également avoir des méthodes concrètes (avec du code).

Les classes abstraites :

  • Ne peuvent pas être instanciées (pas de new).
  • Sont des modèles pour d'autres classes.
  • Permettent de factoriser du code.
  • Doivent être héritée depuis une classe fille.
  • Apporte une sécurité grâce à l'encapsulation.

Abstract UML

<?php

// Classe abstraite, non instanciable
abstract class EtudiantAbstrait
{
    // Force les classes filles à définir cette méthode
    abstract protected function getBlahBlah();
    abstract public function demarrerUneDiscussion($sujet);

    // méthode commune
    public function parler() {
        print $this->getBlahBlah() . "\n";
    }
}

// Classe fille, instanciable car concrète l'ensemble des méthodes possède du code
class EtudiantSIO extends EtudiantAbstrait
{
    private $option = "SLAM";

     protected function getBlahBlah() {
       return "L'informatique c'est cool, je suis : {$this->option}";
     }

     public function demarrerUneDiscussion($sujet) {
       return "Je vais vous parler de « {$sujet} »";
    }
}

// Classe fille, instanciable car concrète l'ensemble des méthodes possède du code
class EtudiantSEN extends EtudiantAbstrait
{
    private $competences = "SOUDER";

     protected function getBlahBlah() {
       return "L'électronique c'est cool, je connais comment {$this->competences}";
     }

     public function demarrerUneDiscussion($sujet) {
       return "Je vais vous parler de « {$sujet} »";
    }
}

// Instanciation
$class1 = new EtudiantSIO();
$class1->parler(); // L'informatique c'est cool, je suis : SLAM
echo $class1->demarrerUneDiscussion('La sécurité') ."\n"; // Je vais vous parler de « La sécurité »

// Le code suivant ne fonctionne pas car on ne peut pas instancier une classe abstraite
$class1 = new EtudiantAbstrait(); // Erreur, on ne peut pas instancier une classe abstraite
Enter fullscreen mode Exit fullscreen mode

// Une classe abstraite, non instanciable
abstract class EtudiantAbstrait
{
    // Force les classes filles à définir cette méthode
    abstract protected String getBlahBlah();
    abstract public String demarrerUneDiscussion(String sujet);

    // méthode commune
    public void parler() {
        print this.getBlahBlah();
    }
}

class EtudiantSIO extends EtudiantAbstrait
{
    private String option = "SLAM";

    @Override
    protected String getBlahBlah() {
      return "L'informatique c'est cool, je suis : {$this->option}";
    }

    @Override
    public String demarrerUneDiscussion(String sujet) {
      return String.format("Moi en SIO, je vais vous parler de « {%s} »", sujet);
    }
}

class EtudiantSEN extends EtudiantAbstrait
{
    private String competences = "SOUDER";

    @Override
    protected String getBlahBlah() {
      return "L'électronique c'est cool, je connais comment {$this->competences}";
    }

    @Override
    public String demarrerUneDiscussion(String sujet) {
      return String.format("Je vais vous parler de « {%s} »", sujet);
    }
}

EtudiantSIO class1 = new EtudiantSIO();
class1.parler();
$class1->demarrerUneDiscussion('La sécurité')
Enter fullscreen mode Exit fullscreen mode
abstract class EtudiantAbstrait
{
    // Force les classes filles à définir cette méthode
    abstract protected string getBlahBlah();
    abstract public string demarrerUneDiscussion(string sujet);

    // méthode commune
    public string parler() {
        Console.WriteLine(this.getBlahBlah());
    }
}

public class EtudiantSIO : EtudiantAbstrait
{
    private string option = "SLAM";

     protected string getBlahBlah() {
       Console.WriteLine("L'informatique c'est cool, je suis : " + option.toString();
     }

     public string demarrerUneDiscussion(string sujet) {
       Console.WriteLine("Je vais vous parler de " + sujet.toString());
    }
}

public class EtudiantSEN : EtudiantAbstrait
{
    private string competences = "SOUDER";

     protected function getBlahBlah() {
       Console.WriteLine("L'électronique c'est cool, je connais comment " + competences.toString());
     }

     public string demarrerUneDiscussion(string sujet) {
       Console.WriteLine("Je vais vous parler de ' " + sujet.toString() + " ' ");
    }
}

EtudiantSIO class1 = new EtudiantSIO();
class1.parler(); // L'électronique c'est cool, je connais comment SOUDER
class1.demarrerUneDiscussion('La sécurité'); // Je vais vous parler de ' La sécurité ' 
Enter fullscreen mode Exit fullscreen mode

Les Interfaces

Une Interface ressemble beaucoup à une classe abstraite. Sauf que celle-ci ne possède pas de code. Une Interface définit un comportement qui devra être implémenté par la classe fille.

Les classes filles implémentent une interface, une classe fille peut implémenter plusieurs interfaces.

Quand une classe implémente une interface, elle doit définir l'ensemble des méthodes de l'interface.

C'est obligatoire. C'est une sorte de contrat entre la classe fille et l'interface.

À quoi sert une interface ? A définir un comportement.

Les interfaces :

  • Ne contiennent que des méthodes publiques.
  • Ne contiennent pas de code.
  • N'est pas instanciable.
  • Son « un contrat » que les classes filles devront implémenter.

UML Interface

// Declaration de l'interface 'Template'
interface Compte
{
    public function deposer($montant);
    public function retirer($montant);
    public function getBalance();
}

class CompteEnLigne implements Compte
{
    private $montant = 0;

    public function deposer($montant){
        $this->montant += $montant;
    }

    public function retirer($montant){
        $this->montant -= $montant;
    }

    public function getBalance() {
        return $montant;
    }
}


$class1 = new CompteEnLigne();
$class1->deposer(1400);
$class1->getBalance(); // 1400

$class1->retirer(400);
$class1->getBalance(); // 1000
Enter fullscreen mode Exit fullscreen mode
// Declaration de l'interface 'Template'
interface Compte
{
    public void deposer(int montant );
    public void retirer(int montant);
    public int getBalance();
}

class CompteEnLigne implements Compte
{
    private int montant = 0;

    @Override
    public void deposer(int montant){
        this.montant += montant;
    }

    @Override
    public void retirer(int montant){
        this.montant -= montant;
    }

    @Override
    public int getBalance() {
        return this.montant;
    }
};


CompteEnLigne class1 = new CompteEnLigne();
class1.deposer(1400);
class1.getBalance(); // 1400

class1.retirer(400);
class1.getBalance(); // 1000
Enter fullscreen mode Exit fullscreen mode
// Declaration de l'interface 'Template'
internal interface Compte {
    fun deposer(montant: Int)
    fun retirer(montant: Int)
    fun getBalance(): Int
}

internal class CompteEnLigne(var contenu: Int = 0) : Compte {
    override fun deposer(montant: Int) {
        contenu += montant
    }

    override fun retirer(montant: Int) {
        contenu -= montant
    }

    override fun balance(): Int {
        return contenu
    }
}

fun main(){
    val class1 = CompteEnLigne();
    class1.deposer(1400);
    class1.getBalance(); // 1400

    class1.retirer(400);
    class1.getBalance(); // 1000
}
Enter fullscreen mode Exit fullscreen mode
// Declaration de l'interface 'Template'
interface Compte
{
    public void deposer(int montant);
    public void retirer(int montant);
    public int getBalance();
}

class CompteEnLigne : Compte
{
    private int montant = 0;

    public void deposer(int montant){
        this.montant += montant;
    }

    public void retirer(int montant){
        this.montant -= montant;
    }

    public int getBalance() {
        return this.montant;
    }
}


CompteEnLigne class1 = new CompteEnLigne();
class1.deposer(1400);
class1.getBalance(); // 1400

class1.retirer(400);
class1.getBalance(); // 1000
Enter fullscreen mode Exit fullscreen mode

Interfaces ou classes abstraites ?

Les interfaces et les classes abstraites remplissent un rôle différent :

  • Les classes abstraites servent à factoriser du code.
  • Les interfaces servent à définir un contrat de service.

Un instant !
L'avantage d'une Interface est qu'il est possible pour une classe d'implémenter plusieurs « contrat » (Interface). Alors que dans la plupart des langages, il n'est pas possible d'hériter de plusieurs classes abstraites.

Redéfinition (Remplacement de méthode)

La redéfinition est la possibilité d’utiliser exactement la même signature pour définir une méthode dans une classe mère et dans une classe fille. Contrairement à la surcharge, la signature (nom et paramètre doivent être identiques).

Concept de redéfinition

La méthode move() remplace donc la définition de celle de la classe mère, et donc son comportement.

Un instant !

  • Nous parlons de redéfinition uniquement dans le cadre de l'héritage.
  • Il ne faut pas confondre la redéfinition avec la surcharge de méthode au sein d'un objet.

tip Les méthodes spécialisées
Il est également possible de spécialiser une méthode, dans ce cas nous ne remplacerons pas complètement la méthode, nous viendrons la compléter en appelant la méthode mère avant notre traitement. Exemple :

class Animal{
  // Reste de la classe
  // …
  public void bruit(){
    System.out.print("BRUUUUIIIITTTT");
  }
  // Reste de la classe
  // …
}
class Humain extends Animal {
  // Reste de la classe
  // …
  @Override
  public void bruit(){
    super.bruit()
    System.out.print(" (Oui mais compréhensible)");
  }
  // Reste de la classe
  // …
}
$humain = new Humain();
$humain.bruit(); // BRUUUUIIIITTTT (Oui mais compréhensible)
class Animal{
  // Reste de la classe
  // …
  public bruit(){
    echo "BRUUUUIIIITTTT";
  }
  // Reste de la classe
  // …
}
class Humain extends Animal {
  // Reste de la classe
  // …
  public bruit(){
    parent::bruit()
    echo " (Oui mais compréhensible)";
  }
  // Reste de la classe
  // …
}
$humain = new Humain();
$humain->bruit(); // BRUUUUIIIITTTT (Oui mais compréhensible)
class Animal {
    // Reste de la classe
    // …
    public fun bruit() {
        print("BRUUUUIIIITTTT")
    }
    // Reste de la classe
    // …
}
class Humain : Animal() {
    // Reste de la classe
    // …
    override fun bruit() {
        super.bruit()
        print(" (Oui mais compréhensible)")
    }
    // Reste de la classe
    // …
}
humain = Humain()
humaim.bruit() // BRUUUUIIIITTTT (Oui mais compréhensible)
class Animal{
  // Reste de la classe
  // …
  public void bruit(){
    Console.WriteLine("BRUUUUIIIITTTT");
  }
  // Reste de la classe
  // …

}
class Humain : Animal {
  // Reste de la classe
  // …
  public override void bruit() {
    base.bruit()
    Console.WriteLine(" (Oui mais compréhensible)");
  }
  // Reste de la classe
  // …
}
Humain humain = new Humain();
humain.bruit(); // BRUUUUIIIITTTT (Oui mais compréhensible)

Polymorphisme

Le polymorphisme peut être vu comme la capacité de choisir dynamiquement la méthode qui correspond au type réel de l’objet.

  • Si la classe B hérite de la classe A

    • Classe B "EST-UN" Classe A
    • Toutes les méthodes de la classe A peuvent donc être appelées sur la classe B.
  • Le polymorphisme nous permettra :

    • Manipuler un objet sans en connaître le type précis.
    • Manipulation de liste sans connaître le type des objets.

Pratique !
Grâce au polymorphisme, nous allons pouvoir créer des array (liste, tableau …) avec des objets de types différents. Exemple :

abstract class MachineVolante {
    public void fly()
}
class Mig29 extends MachineVolante {
    @Override
    public void fly() {
        out.println("Start, fly");
    }
    public void bombardment() {
        out.println("Fire missile");
    }
}
class Helicoptere extends MachineVolante {
    @Override
    public void fly() {
        out.println("Start vertically, hover, fly");
    }
}
// La liste est du type de la classe mère
List<MachineVolante> machines = new ArrayList<MachineVolante>();
machines.add(new MachineVolante());
machines.add(new Mig29());
machines.add(new Helicoptere());
machines.add(new Mig29());
// On boucle sans en connaitre le type
for (MachineVolante m : machines) {
    m.fly();
}
abstract class MachineVolante {
    public void fly()
}
class Mig29 : MachineVolante {

    public override void fly() {
        Console.WriteLine("Start, fly");
    }
    public void bombardment() {
        Console.WriteLine("Fire missile");
    }
}
class Helicoptere : MachineVolante {
    public void fly() : base() {
        Console.WriteLine("Start vertically, hover, fly");
    }
}
// La liste est du type de la classe mère
List<MachineVolante> machines = new List<MachineVolante>();
machines.add(new MachineVolante()); // Une erreur apparait ici
machines.add(new Mig29());
machines.add(new Helicoptere());
machines.add(new Mig29());
// On boucle sans en connaitre le type
foreach (MachineVolante m in machines) {
    m.fly();
}

Les namespaces (organisation des classes)

Les namespaces permettent d'organiser les classes en groupes. Cela permet de mieux s'y retrouver dans un projet et de mieux gérer les dépendances. Les namespaces sont des espaces de noms. Ils permettent de regrouper des classes ayant un nom identique, mais qui ne sont pas les mêmes. Par exemple, si vous avez une classe Personne dans votre projet, vous pouvez avoir une classe Personne dans un namespace Mammifere et une classe Personne dans un namespace Primate. Les deux classes Personne ne seront pas les mêmes.

namespace Mammifere\Primate ;

class Personne { // etc...
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)