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 où toutes les fonctions membres sont virtuelles pures. 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 d'implémentation aura des méthodes draw() et resize(), mais ne dit rien sur la façon dont elles fonctionnent. Cette séparation est puissante - le code peut dépendre de l'interface sans rien savoir 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, permettant 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 le couplage faible - votre code dépend d'abstractions plutôt que d'implémentations concrètes, ce qui le rend plus facile à étendre et à tester.

challenge icon

Défi

Facile

Construisons un système de gestion d'appareils qui démontre comment les interfaces définissent des contrats pour différents types d'appareils. Vous allez créer deux interfaces distinctes que les appareils peuvent implémenter, puis construire des classes d'appareils concrètes qui remplissent l'un ou l'autre de ces contrats, ou les deux.

Vous organiserez votre code sur trois fichiers :

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

    Powerable — tout appareil qui peut être allumé et éteint :

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

    Connectable — tout appareil qui peut 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() retournant un std::string
    • Un destructeur virtuel
  • Devices.h : Implémentez des classes d'appareils concrètes qui utilisent ces interfaces :

    Lamp — implémente uniquement Powerable :

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

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

    • Membres privés : bool isOn (commence à false), std::string brand, std::string currentNetwork (commence vide)
    • Un constructeur prenant la marque du téléviseur
    • powerOn() définit isOn à true et affiche : <brand> TV: Powered on
    • powerOff() définit isOn à false, efface le réseau et affiche : <brand> TV: Powered off
    • getPowerStatus() retourne "ON" ou "OFF"
    • connect() stocke le nom du réseau et affiche : <brand> TV: Connected to <network>
    • disconnect() efface le réseau et affiche : <brand> TV: Disconnected
    • getConnectionInfo() retourne "Connected to <network>" s'il est connecté, ou "Not connected" s'il est vide
  • main.cpp : Lit trois entrées (chacune sur une ligne séparée) :
    1. Nom de la lampe
    2. Marque de la TV
    3. Nom du réseau

    Créez une Lamp et une SmartTV. Démontrez comment la même interface peut être utilisée avec différents appareils :

    Tout d'abord, travaillez avec les deux appareils via l'interface Powerable. Stockez des pointeurs vers les deux dans un tableau de Powerable*, puis bouclez et appelez powerOn() sur chacun, suivi de l'affichage de leur statut sous la forme : Status: <powerStatus>

    Affichez une ligne vide, puis travaillez avec la SmartTV via l'interface Connectable. Créez un pointeur Connectable* vers votre SmartTV, appelez connect() avec le nom du réseau, et affichez : Connection: <connectionInfo>

    Affichez une autre ligne vide, puis éteignez les deux appareils via le tableau Powerable et affichez leur statut 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 la SmartTV peut être traitée soit comme un Powerable soit comme un Connectable selon le pointeur d'interface que vous utilisez. Cette flexibilité est la force de l'implémentation de plusieurs interfaces — votre code peut fonctionner avec n'importe quel appareil qui remplit le contrat dont il a besoin, sans connaître le type concret.

Aide-mémoire

Une interface en C++ est une classe abstraite où toutes les fonctions membres sont virtuelles pures. Les interfaces définissent uniquement les opérations qui doivent exister sans aucune implémentation :

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

Les classes implémentent des interfaces en redéfinissant toutes les fonctions virtuelles pures :

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; }
};

Une classe peut implémenter plusieurs interfaces en utilisant l'héritage multiple :

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 le couplage faible en permettant au code de dépendre d'abstractions plutôt que d'implémentations concrètes.

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 la SmartTV
    // Appeler connect() avec le nom du réseau
    // Afficher : 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