Menu
Coddy logo textTech

Design de Interfaces em C++

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 61 de 104.

Uma interface em C++ é uma classe abstrata onde todas as funções membro são virtuais puras. Ao contrário de classes abstratas que podem conter alguma implementação, as interfaces definem apenas quais operações devem existir - elas especificam um contrato sem qualquer comportamento.

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

Esta interface Drawable garante que qualquer classe implementadora terá os métodos draw() e resize(), mas não diz nada sobre como eles funcionam. Esta separação é poderosa - o código pode depender da interface sem saber nada sobre os tipos concretos.

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 { /* redimensionar botão */ }
};

Uma classe pode implementar múltiplas interfaces, permitindo designs flexíveis onde objetos podem desempenhar diferentes papéis:

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

Interfaces promovem baixo acoplamento - seu código depende de abstrações em vez de implementações concretas, tornando-o mais fácil de estender e testar.

challenge icon

Desafio

Fácil

Vamos construir um sistema de gerenciamento de dispositivos que demonstra como as interfaces definem contratos para diferentes tipos de dispositivos. Você criará duas interfaces separadas que os dispositivos podem implementar e, em seguida, construirá classes de dispositivos concretas que cumprem um ou ambos os contratos.

Você organizará seu código em três arquivos:

  • Interfaces.h: Defina duas classes abstratas puras (interfaces) que representam diferentes capacidades de dispositivos:

    Powerable — qualquer dispositivo que possa ser ligado e desligado:

    • Um método virtual puro powerOn()
    • Um método virtual puro powerOff()
    • Um método virtual puro getPowerStatus() retornando uma std::string
    • Um destrutor virtual

    Connectable — qualquer dispositivo que possa se conectar a uma rede:

    • Um método virtual puro connect(const std::string& network)
    • Um método virtual puro disconnect()
    • Um método virtual puro getConnectionInfo() retornando uma std::string
    • Um destrutor virtual
  • Devices.h: Implemente classes de dispositivos concretas que usam essas interfaces:

    Lamp — implementa apenas Powerable:

    • Um membro privado bool isOn (começa como false)
    • Um membro privado std::string name
    • Um construtor que recebe o nome da lâmpada
    • powerOn() define isOn como true e imprime: <name>: Light turned on
    • powerOff() define isOn como false e imprime: <name>: Light turned off
    • getPowerStatus() retorna "ON" ou "OFF" com base no estado

    SmartTV — implementa tanto Powerable quanto Connectable:

    • Membros privados: bool isOn (começa false), std::string brand, std::string currentNetwork (começa vazio)
    • Um construtor que recebe a marca da TV
    • powerOn() define isOn como true e imprime: <brand> TV: Powered on
    • powerOff() define isOn como false, limpa a rede e imprime: <brand> TV: Powered off
    • getPowerStatus() retorna "ON" ou "OFF"
    • connect() armazena o nome da rede e imprime: <brand> TV: Connected to <network>
    • disconnect() limpa a rede e imprime: <brand> TV: Disconnected
    • getConnectionInfo() retorna "Connected to <network>" se estiver conectado, ou "Not connected" se estiver vazio
  • main.cpp: Leia três entradas (cada uma em uma linha separada):
    1. Nome da lâmpada
    2. Marca da TV
    3. Nome da rede

    Crie uma Lamp e uma SmartTV. Demonstre como a mesma interface pode ser usada com diferentes dispositivos:

    Primeiro, trabalhe com ambos os dispositivos através da interface Powerable. Armazene ponteiros para ambos em um array de Powerable*, então percorra o array e chame powerOn() em cada um, seguido pela impressão de seus status como: Status: <powerStatus>

    Imprima uma linha em branco e, em seguida, trabalhe com a SmartTV através da interface Connectable. Crie um ponteiro Connectable* para sua SmartTV, chame connect() com o nome da rede e imprima: Connection: <connectionInfo>

    Imprima outra linha em branco e, em seguida, desligue ambos os dispositivos através do array Powerable e mostre seus status finais.

Por exemplo, com as entradas Desk Lamp, Samsung e 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

Observe como a SmartTV pode ser tratada como Powerable ou Connectable, dependendo de qual ponteiro de interface você usa. Essa flexibilidade é o poder de implementar múltiplas interfaces — seu código pode funcionar com qualquer dispositivo que cumpra o contrato de que ele precisa, sem conhecer o tipo concreto.

Folha de consulta

Uma interface em C++ é uma classe abstrata onde todas as funções membros são virtuais puras. Interfaces definem apenas quais operações devem existir sem qualquer implementação:

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

As classes implementam interfaces sobrescrevendo todas as funções virtuais puras:

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

Uma classe pode implementar múltiplas interfaces usando herança múltipla:

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

Interfaces promovem o baixo acoplamento, permitindo que o código dependa de abstrações em vez de implementações concretas.

Experimente você mesmo

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

using namespace std;

int main() {
    // Ler as entradas
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: Criar um objeto Lamp e um objeto SmartTV
    
    // TODO: Criar um array de ponteiros Powerable* contendo ambos os dispositivos
    
    // TODO: Percorrer o array e chamar powerOn() em cada dispositivo
    // Após cada powerOn(), imprimir: Status: <powerStatus>
    
    // TODO: Imprimir uma linha em branco
    
    // TODO: Criar um ponteiro Connectable* para a SmartTV
    // Chamar connect() com o nome da rede
    // Imprimir: Connection: <connectionInfo>
    
    // TODO: Imprimir uma linha em branco
    
    // TODO: Percorrer o array Powerable e chamar powerOff() em cada dispositivo
    // Após cada powerOff(), imprimir: Status: <powerStatus>
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos