Aller au contenu

đŸ§© Programmation objet⚓

1. 🔑 Principes⚓

Comme son nom l’indique, le paradigme objet donne une vision du problĂšme Ă  rĂ©soudre comme un ensemble d’objets. Ces objets ont des caractĂ©ristiques et des comportements qui leurs sont propres, ce sont respectivement les attributs et les mĂ©thodes. Les objets interagissent entre eux avec des messages, en respectant leur interface (c’est-Ă -dire l’ensemble des spĂ©cifications en version compacte, les signatures des mĂ©thodes, on verra ce point plus en dĂ©tail tout au long de l’annĂ©e).

diagramme de classe

1.1 📖 Un exemple « papier »⚓

Créons un jeu vidéo de type « hack and slash ». Dans ce type de jeu, le personnage joué doit tuer un maximum de monstres sur une carte de jeu. A lire le descriptif, 3 objets apparaissent naturellement :

  • đŸ§™â€â™‚ïž Le personnage principal
  • đŸ‘č Les monstres
  • đŸ—ș La carte

On remarque immĂ©diatement que « monstre » aurait plutĂŽt tendance Ă  dĂ©signer un type qu’un objet unique : les objets sont tous typĂ©s. Le type dĂ©finit Ă  la fois le nom de l’objet, et ce qu’il fait.

De mĂȘme, avec cette structure, on peut avoir plusieurs objets « personnages », pour jouer en multi-joueur, et bien sĂ»r plusieurs cartes.

1.2 đŸ—ïž Classe et objets⚓

Le « moule » avec lequel on va fabriquer un objet est appelé classe.

La classe personnage comprend par exemple les attributs :

  • ❀ Points de vie
  • ⚔ DĂ©gĂąts maximums qu’inflige le personnage
  • 📍 Position

Elle comprend les méthodes :

  • đŸš¶ DĂ©placement
  • đŸ—Ąïž Attaque

Et des mĂ©thodes qui permettent d’accĂ©der aux attributs, ou bien de les modifier. Ce sont les accesseurs et les mutateurs. Les attributs sont cachĂ©s des objets extĂ©rieurs (le principe est l’encapsulation), ils sont privĂ©s. Les mĂ©thodes permettant d’y accĂ©der sont Ă  l’inverse publiques. Un objet extĂ©rieur ne doit pas pouvoir modifier Ă  loisir les attributs d’un autre objet, en effet il doit y avoir un contrĂŽle de l’objet sur ses propres attributs.

Quand on crĂ©e un personnage, l’ordinateur crĂ©e une instance de la classe. C’est-Ă -dire que tous les objets de la classe auront les mĂȘmes attributs et mĂ©thodes, mais que deux objets de la mĂȘme classe peuvent avoir des valeurs diffĂ©rentes pour les attributs. L’instance est créée grĂące Ă  un constructeur.

1.3 🏡 MĂ©taphore⚓

  • 📐 Une classe, c’est le plan d’une maison (abstrait)
  • 🏠 Un objet, c’est une maison issue du plan (concret). Ce qu’il y a Ă  l’intĂ©rieur d’une maison diffĂšre de l’intĂ©rieur d’une autre maison (dĂ©coration, mobilier, etc
)
  • 🔘 L’interface c’est le bouton qui permet de rĂ©gler le chauffage
  • 🔧 L’implĂ©mentation (ou la rĂ©alisation) de l’interface, c’est la mĂ©thode de chauffage/climatisation retenue. L’utilisateur ne connaĂźt pas le dĂ©tail de l’implĂ©mentation, ce qui compte pour lui, c’est le bouton de rĂ©glage (donc l’interface)

2. Concepts de classe, attributs, mĂ©thodes⚓

2.1 ☕ Une classe en Java⚓

Le code suivant montre, Ă©tape par Ă©tape, la classe « personnage » telle qu’on l’a dĂ©finie au paragraphe prĂ©cĂ©dent.

Le mot-clé pour définir une classe est class. On donne ensuite les spécifications de la classe (on documente).

Une classe a une visibilité :

  • 🌍 public : le mot class est alors prĂ©cĂ©dĂ© de public, tout utilisateur qui importe le paquetage peut utiliser la classe. Dans ce cas elle doit ĂȘtre dĂ©finie dans un fichier qui a pour nom le nom de la classe.
  • 🔒 privĂ© : le mot class est alors prĂ©cĂ©dĂ© de private, seules des classes dĂ©finies dans le mĂȘme fichier peuvent utiliser cette classe.
  • 📩 paquetage : le mot class n’est pas prĂ©cĂ©dĂ© de mot particulier, toutes les classes du paquetage peuvent utiliser la classe.

Java
public class Personnage {

    """
    Personnage d'un jeu de type hack 'n slash

    Attributs :
        nom : chaine de caractĂšres, nom du personnage
        pv : entier positif ou nul, points de vie du personnage
        degats : entier strictement positif, dégats maximum du personnage
        position : couple d'entiers donnant l'abscisse et l'ordonnée
                   du personnage sur la carte
    Méthodes:
        Init() constructeur de la classe Personnage
        getAttribut() : accesseurs des attributs
        setAttributs(nouvelle_valeur) : mutateurs des attributs.
                Uniquement pour les attributs _pv et _position
        deplacement(paramĂštres) : permet de changer la position
                                  du personnage
        attaque() : renvoie les dégùts faits à l'adversaire
    """
La premiĂšre mĂ©thode dans la classe est le constructeur, appelĂ© du mĂȘme nom que la classe en JAVA. Dans le constructeur de Personnage est aussi passĂ© en argument le paramĂštre nom. Le constructeur initialise les attributs de l’objet (points de vie, dĂ©gĂąts, position).

Tous les attributs sont prĂ©cĂ©dĂ©s d’un tiret bas « _ » pour signifier qu’ils sont privĂ©s.

Java
Personnage(String nom) {
    """
    Constructeur de la classe Personnage

    Données:   
        nom : chaine de caractĂšres, nom du personnage
        pv : entier positif ou nul, points de vie du personnage
        degats : entier strictement positif, dégats maximum du personnage
        position : couple d'entiers donnant l'abscisse et
                   l'ordonnée du personnage sur la carte
    Résultat : 
        ne retourne rien, crée un nouveau Personnage
    """
    nom = nom;
    pv = 80;
    degats = 8;
    position = (0,0);
}

ou encore :

Java
this.nom = nom;

Le mot-clé this désigne en permanence l'objet dans lequel on se trouve. Il existe dÚs l'instant que l'on se trouve dans l'instance de quelque chose. En particulier, il n'est pas défini dans un élément statique.

En gĂ©nĂ©ral, ce mot-clĂ© est utilisĂ© pour lever l'ambigĂŒitĂ© qui peut exister entre le champ d'une classe et un paramĂštre d'une mĂ©thode dans laquelle on se trouve, comme dans l'exemple qui suit.

âžĄïž Exemple : Utilisation de this pour lever l'ambiguĂŻtĂ© entre un champ et un paramĂštre

illustration this

2.2 🔑 Accesseurs (getters) et Mutateurs (setters)⚓

Les accesseurs n’ont pas de paramĂštre, les mutateurs ont la nouvelle valeur. Il n’y a pas forcĂ©ment de mutateurs (ni d’accesseurs) pour tous les attributs : 👉 par exemple, le nom du personnage n’est pas modifiable ici.

On ne documente pas les accesseurs, on peut le faire pour les mutateurs.

đŸ“„ Accesseurs des attributs⚓

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

public int getPv() {
    return this.pv;
}

public int getDegats() {
    return this.degats;
}

public [] getPosition() {
    return this.position;
}

đŸ“€ Mutateurs des attributs⚓

Java
public void setPv(int nouveaux_pv) {
    """
    Les points de vie d’un personnage sont positifs ou nuls.
    """
    if (nouveaux_pv < 0) {
        pv = 0;
    } else {
        pv = nouveaux_pv;
    }
}

public void setDegats(int nouveaux_degats) {
    degats = nouveaux_degats;
}

2.3 đŸ› ïž DĂ©finition d’une mĂ©thode⚓

Une méthode est définie par :

  • đŸ·ïž Son type de retour : type de la valeur retournĂ©e par la mĂ©thode. Si la mĂ©thode ne retourne pas de valeur le type spĂ©cifiĂ© est alors void. Le type retournĂ© peut ĂȘtre un tableau.
  • 📝 Son nom
  • 🔱 Ses paramĂštres :

  • spĂ©cifiĂ©s par leur type et leur nom

  • sĂ©parĂ©s par des virgules
  • tous transmis par valeur (mais la plupart sont des rĂ©fĂ©rences)

On peut avoir un paramĂštre ellipse (...) qui est en fait un tableau, et doit ĂȘtre le dernier paramĂštre.

Exemple⚓

Java
void m(X ... e) {
    // e est un tableau à une dimension d'éléments de type X
}
Un appel de la méthode m se fait alors de la façon suivante :

Java
m(null);
m();
m(x1);
m(x1, x2);

✍ Signature d’une mĂ©thode⚓

Une méthode est caractérisée par sa signature :

  • đŸ”č son nom
  • đŸ”č la liste des types des paramĂštres, dans l'ordre

👉 Deux mĂ©thodes diffĂ©rentes ne peuvent pas avoir la mĂȘme signature. 👉 Deux mĂ©thodes ayant le mĂȘme nom mais des paramĂštres diffĂ©rents sont des surcharges.

đŸ‘ïž VisibilitĂ© d’une mĂ©thode⚓

  • 🌍 public : accessible partout
  • 🔒 private : utilisable seulement dans la classe
  • đŸ›Ąïž protected : utilisable dans la classe et les classes dĂ©rivĂ©es
  • 📩 paquetage : utilisable dans toutes les classes du mĂȘme paquetage

illustration classe Personnage

2.4 đŸ·ïž Attributs⚓

Un attribut se définit en donnant son type, puis son nom, ([] pour un tableau), et éventuellement une partie initialisation.

đŸ‘ïž VisibilitĂ© d’un attribut⚓

  • 🌍 public : sa dĂ©finition est prĂ©cĂ©dĂ©e de public, et il peut ĂȘtre utilisĂ© par tout utilisateur de la classe.
  • 🔒 privĂ© : sa dĂ©finition est prĂ©cĂ©dĂ©e de private, et il ne peut ĂȘtre utilisĂ© qu’à l’intĂ©rieur de la classe.
  • đŸ›Ąïž protĂ©gĂ© : sa dĂ©finition est prĂ©cĂ©dĂ©e de protected, et il ne peut ĂȘtre utilisĂ© qu’à l’intĂ©rieur de la classe, ou des classes dĂ©rivĂ©es.
  • 📩 paquetage : l’attribut peut ĂȘtre utilisĂ© dans toute classe du mĂȘme paquetage.

📌 À l’intĂ©rieur de la dĂ©finition d’une mĂ©thode, l’accĂšs Ă  un attribut se fait soit directement, soit en prĂ©fixant l'attribut par this qui est une rĂ©fĂ©rence Ă  l’objet pour lequel est appelĂ©e la mĂ©thode.

📌 À l’extĂ©rieur de la dĂ©finition de la classe, l’accĂšs se fait en Ă©crivant : 👉 nom de l’instance . nom de l’attribut ou nom de l’instance . nom de la mĂ©thode

Exemple

Java
class Point {
   private int x;                
   public int y;

   void f() {
      x = 0;
      this.y = 1;
   }     
}

En dehors de la définition de la classe :

Java
Point p = new Point();

p.x = 0; // ❌ Erreur de compilation : la visibilitĂ© de x est privĂ©e !
p.y = 1; // ✅

🔒 Attributs publics, attributs privĂ©s⚓

En programmation objet, indĂ©pendamment du langage, on considĂšre que les attributs doivent ĂȘtre privĂ©s, encapsulĂ©s Ă  l’intĂ©rieur de la classe et accessibles uniquement par mutateurs.

Vous trouverez sur le web de nombreux exemples de code rĂ©digĂ©s de cette maniĂšre, sans forcĂ©ment savoir si les propriĂ©tĂ©s avancĂ©es de Python ont Ă©tĂ© utilisĂ©es. Nous utiliserons Ă©galement ce type de code plus tard dans l’annĂ©e, pour simplifier l’écriture des programmes.

⚠ Remarques

  • ❌ Ne pas donner le mĂȘme nom Ă  une mĂ©thode et Ă  un attribut dans une classe !
  • ✅ Plusieurs classes peuvent avoir les mĂȘmes noms de mĂ©thodes sans problĂšme.

👉 En effet, l’appel d’une mĂ©thode passe par :

Java
objet.méthode()
Ce qui permet de savoir dans quelle classe chercher la méthode.

âžĄïž La classe dĂ©finit son espace de noms.

3. 🆕 CrĂ©ation d’instances⚓

Pour créer une instance de la classe A, on écrira :

Java
A a;        // définition de la variable référence
a = new A(); // crĂ©ation de l’instance

3.1 🔄 Étapes de crĂ©ation avec new⚓

La crĂ©ation d’une instance par l’opĂ©rateur new se dĂ©roule en trois temps :

  1. 📩 RĂ©servation de l’espace mĂ©moire suffisamment grand pour reprĂ©senter l’objet.
  2. ⚡ Appel du constructeur de l’objet → Initialisation des attributs et d’une rĂ©fĂ©rence Ă  l’objet reprĂ©sentant la classe de l’instance en train d’ĂȘtre créée.
  3. 🔗 Renvoi d’une rĂ©fĂ©rence sur l’objet nouvellement créé.

Exemple

Java
class Point {
   int x;
   int y;
   ...
}

Point a, b;
a = new Point(...);
b = a;
illustration

3.2 đŸ—‘ïž Destruction d’instances⚓

La destruction des instances se fait automatiquement par un thread, le garbage collector, qui cherche tous les objets qui ne sont plus référencés et les supprime.

👉 Le garbage collector peut ĂȘtre appelĂ© directement par :

Java
System.gc();

📊 rĂ©sumĂ© POO

Concept Idées Générales
🔑 Principes
Objets = attributs (caractĂ©ristiques) + mĂ©thodes (comportements), Encapsulation : attributs privĂ©s, accĂšs via getters/setters, Interface = ce qu’on voit (contrat), ImplĂ©mentation = ce qui est fait en interne
đŸ—ïž Classe et Objet Classe = modĂšle / plan 📝, Objet = instance concrĂšte 🏠, CrĂ©ation → via un constructeur,
Destruction → via le Garbage Collector đŸ—‘ïž
☕ Java (exemple) Mot-clĂ© class, VisibilitĂ© : public/private/protected/package, MĂ©thodes → dĂ©finies par type retour + nom + paramĂštres, Signature = nom + liste des paramĂštres, Surcharge = mĂȘme nom, paramĂštres diffĂ©rents
📩 Attributs Toujours privĂ©s (bonnes pratiques), AccĂšs uniquement via mutateurs (setters)

infographie

4. 🧬 HĂ©ritage⚓

L’hĂ©ritage permet de crĂ©er une nouvelle classe (dite classe fille) Ă  partir d’une classe existante (dite classe mĂšre). La classe fille hĂ©rite :

  • des attributs de la classe mĂšre
  • des mĂ©thodes de la classe mĂšre

Elle peut aussi :

  • 🔄 redĂ©finir (surcharger) certaines mĂ©thodes
  • ➕ ajouter ses propres attributs et mĂ©thodes

👉 Cela favorise la rĂ©utilisation du code et l’organisation hiĂ©rarchique des classes.

Exemple en Java⚓

Java
// Classe mĂšre
class Personnage {
    protected String nom;
    protected int pv;

    public Personnage(String nom, int pv) {
        this.nom = nom;
        this.pv = pv;
    }

    public void afficher() {
        System.out.println(nom + " possĂšde " + pv + " points de vie.");
    }
}

// Classe fille
class Guerrier extends Personnage {
    private int force;

    public Guerrier(String nom, int pv, int force) {
        super(nom, pv); // appel du constructeur de la classe mĂšre
        this.force = force;
    }

    @Override
    public void afficher() {
        System.out.println(nom + " (Guerrier) possĂšde " + pv + 
                           " points de vie et " + force + " de force.");
    }
}

📝 Remarques⚓

  • super appelle le constructeur ou une mĂ©thode de la classe mĂšre.
  • Les attributs hĂ©ritĂ©s peuvent ĂȘtre accessibles selon leur visibilitĂ© (protected ou public).
  • Une classe peut ĂȘtre dĂ©clarĂ©e final → empĂȘche l’hĂ©ritage.

5. 🎭 Polymorphisme⚓

Le polymorphisme est la capacitĂ© d’un objet Ă  prendre plusieurs formes. Dans le cadre de la POO :

  • Plusieurs classes filles hĂ©ritant d’une mĂȘme classe mĂšre peuvent ĂȘtre manipulĂ©es comme si elles Ă©taient de la classe mĂšre.
  • Les mĂ©thodes redĂ©finies s’exĂ©cutent en fonction de l’objet rĂ©el.

👉 Cela permet d’écrire du code plus gĂ©nĂ©rique et rĂ©utilisable.

Exemple en Java⚓

Java
class Personnage {
    protected String nom;
    public Personnage(String nom) { this.nom = nom; }
    public void attaquer() {
        System.out.println(nom + " attaque avec ses poings.");
    }
}

class Guerrier extends Personnage {
    public Guerrier(String nom) { super(nom); }
    @Override
    public void attaquer() {
        System.out.println(nom + " attaque avec une épée !");
    }
}

class Mage extends Personnage {
    public Mage(String nom) { super(nom); }
    @Override
    public void attaquer() {
        System.out.println(nom + " lance un sort de feu !");
    }
}

// Exemple d'utilisation
Personnage p1 = new Guerrier("Arthas");
Personnage p2 = new Mage("Merlin");

p1.attaquer(); // Arthas attaque avec une épée !
p2.attaquer(); // Merlin lance un sort de feu !

📝 Remarques⚓

  • On peut manipuler une liste d’objets de type Personnage et exĂ©cuter attaquer() sans savoir si c’est un Guerrier ou un Mage.
  • Ce mĂ©canisme repose sur la liaison dynamique (mĂ©thode choisie Ă  l’exĂ©cution).
  • On distingue :

  • Polymorphisme d’hĂ©ritage (comme ci-dessus)

  • Polymorphisme d’interface (implĂ©mentation de contrats communs).