Menu
Coddy logo textTech

Conception d’interfaces en C++

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 61 sur 104.

Une interface en C++ est une classe abstraite dont chaque fonction membre—à l’exception du destructeur (virtuel)—est virtuelle pure. Contrairement aux classes abstraites qui peuvent contenir une certaine implémentation, les interfaces définissent uniquement les opérations qui doivent exister : elles spécifient un contrat sans aucun comportement.

class Drawable {
public:
    virtual void draw() = 0;
    virtual void resize(double factor) = 0;
    virtual ~Drawable() = default;
};

Cette interface Drawable garantit que toute classe qui l’implémente possède les méthodes draw() et resize(), mais ne dit rien sur leur fonctionnement. Cette séparation est puissante : le code peut dépendre de l’interface sans rien connaître des types concrets.

class Circle : public Drawable {
    double radius;
public:
    Circle(double r) : radius(r) {}
    void draw() override { std::cout << "Drawing circle" << std::endl; }
    void resize(double factor) override { radius *= factor; }
};

class Button : public Drawable {
    std::string label;
public:
    Button(std::string l) : label(l) {}
    void draw() override { std::cout << "Drawing button: " << label << std::endl; }
    void resize(double factor) override { /* redimensionner le bouton */ }
};

Une classe peut implémenter plusieurs interfaces, ce qui permet des conceptions flexibles où les objets peuvent remplir différents rôles :

class Clickable {
public:
    virtual void onClick() = 0;
    virtual ~Clickable() = default;
};

class IconButton : public Drawable, public Clickable {
public:
    void draw() override { std::cout << "Drawing icon" << std::endl; }
    void resize(double factor) override { }
    void onClick() override { std::cout << "Clicked!" << std::endl; }
};

Les interfaces favorisent un couplage faible : votre code dépend d’abstractions plutôt que d’implémentations concrètes, ce qui facilite son extension et ses tests.

challenge icon

Défi

Facile

Construisons un système de gestion d’appareils qui montre comment les interfaces définissent des contrats pour différents types d’appareils. Vous créerez deux interfaces distinctes que les appareils pourront implémenter, puis vous construirez des classes d’appareils concrètes qui respecteront l’un ou les deux de ces contrats.

Vous organiserez votre code sur trois fichiers :

  • Interfaces.h : définir deux classes abstraites pures (interfaces) qui représentent différentes capacités d’appareils :

    Powerable : tout appareil pouvant être allumé et éteint :

    • Une méthode virtuelle pure powerOn()
    • Une méthode virtuelle pure powerOff()
    • Une méthode virtuelle pure getPowerStatus() renvoyant une std::string
    • Un destructeur virtuel

    Connectable : tout appareil pouvant se connecter à un réseau :

    • Une méthode virtuelle pure connect(const std::string& network)
    • Une méthode virtuelle pure disconnect()
    • Une méthode virtuelle pure getConnectionInfo() renvoyant une std::string
    • Un destructeur virtuel
  • Devices.h : implémenter des classes d’appareils concrètes qui utilisent ces interfaces :

    Lamp : implémente uniquement Powerable :

    • Un membre privé bool isOn (initialisé à false)
    • Un membre privé std::string name
    • Un constructeur prenant le nom de la lampe
    • powerOn() affecte la valeur true à isOn et affiche : <name>: Light turned on
    • powerOff() affecte la valeur false à isOn et affiche : <name>: Light turned off
    • getPowerStatus() renvoie "ON" ou "OFF" selon l’état

    SmartTV : implémente à la fois Powerable et Connectable :

    • Membres privés : bool isOn (initialisé à false), std::string brand, std::string currentNetwork (initialisé à une chaîne vide)
    • Un constructeur prenant la marque du téléviseur
    • powerOn() affecte la valeur true à isOn et affiche : <brand> TV: Powered on
    • powerOff() affecte la valeur false à isOn, efface le réseau et affiche : <brand> TV: Powered off
    • getPowerStatus() renvoie "ON" ou "OFF"
    • connect() enregistre le nom du réseau et affiche : <brand> TV: Connected to <network>
    • disconnect() efface le réseau et affiche : <brand> TV: Disconnected
    • getConnectionInfo() renvoie "Connected to <network>" si l’appareil est connecté, ou "Not connected" si le réseau est vide
  • main.cpp : lire trois entrées (chacune sur une ligne distincte) :
    1. Nom de la lampe
    2. Marque du téléviseur
    3. Nom du réseau

    Créer une Lamp et un SmartTV. Montrer comment une même interface peut être utilisée avec différents appareils :

    Commencer par travailler avec les deux appareils via l’interface Powerable. Stocker des pointeurs vers les deux appareils dans un tableau de Powerable*, puis parcourir ce tableau et appeler powerOn() sur chaque appareil, avant d’afficher leur état sous la forme : Status: <powerStatus>

    Afficher une ligne vide, puis travailler avec le SmartTV via l’interface Connectable. Créer un pointeur Connectable* vers votre SmartTV, appeler connect() avec le nom du réseau, puis afficher : Connection: <connectionInfo>

    Afficher une autre ligne vide, puis éteindre les deux appareils via le tableau Powerable et afficher leur état final.

Par exemple, avec les entrées Desk Lamp, Samsung et HomeWiFi :

Desk Lamp: Light turned on
Status: ON
Samsung TV: Powered on
Status: ON

Samsung TV: Connected to HomeWiFi
Connection: Connected to HomeWiFi

Desk Lamp: Light turned off
Status: OFF
Samsung TV: Powered off
Status: OFF

Remarquez comment le SmartTV peut être traité comme un Powerable ou comme un Connectable, selon le pointeur d’interface que vous utilisez. Cette flexibilité constitue l’intérêt d’implémenter plusieurs interfaces. Votre code peut fonctionner avec tout appareil qui respecte le contrat dont il a besoin, sans connaître son type concret.

Essayez vous-même

#include <iostream>
#include <string>
#include "Devices.h"

using namespace std;

int main() {
    // Lire les entrées
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: Créer un objet Lamp et un objet SmartTV
    
    // TODO: Créer un tableau de pointeurs Powerable* contenant les deux appareils
    
    // TODO: Parcourir le tableau et appeler powerOn() sur chaque appareil
    // Après chaque powerOn(), afficher : Status: <powerStatus>
    
    // TODO: Afficher une ligne vide
    
    // TODO: Créer un pointeur Connectable* vers le SmartTV
    // Appeler connect() avec le nom du réseau
    // Print: Connection: <connectionInfo>
    
    // TODO: Afficher une ligne vide
    
    // TODO: Parcourir le tableau Powerable et appeler powerOff() sur chaque appareil
    // Après chaque powerOff(), afficher : Status: <powerStatus>
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur C++ en ligne