Menu
Coddy logo textTech

Diseño de interfaces en C++

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 61 de 104.

Una interfaz en C++ es una clase abstracta en la que cada función miembro—excepto el destructor (virtual)—es virtual pura. A diferencia de las clases abstractas, que pueden contener parte de la implementación, las interfaces solo definen qué operaciones deben existir: especifican un contrato sin ningún comportamiento.

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

Esta interfaz Drawable garantiza que cualquier clase que la implemente tendrá los métodos draw() y resize(), pero no dice nada sobre cómo funcionan. Esta separación es poderosa: el código puede depender de la interfaz sin saber nada sobre los 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ón */ }
};

Una clase puede implementar múltiples interfaces, lo que permite diseños flexibles en los que los objetos pueden cumplir diferentes roles:

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

Las interfaces promueven un acoplamiento flexible: tu código depende de abstracciones en lugar de implementaciones concretas, lo que facilita su extensión y prueba.

challenge icon

Desafío

Fácil

Construyamos un sistema de gestión de dispositivos que demuestre cómo las interfaces definen contratos para distintos tipos de dispositivos. Crearás dos interfaces independientes que los dispositivos pueden implementar y, después, construirás clases concretas de dispositivos que cumplan uno o ambos contratos.

Organizarás tu código en tres archivos:

  • Interfaces.h: Define dos clases abstractas puras (interfaces) que representan distintas capacidades de los dispositivos:

    Powerable: cualquier dispositivo que pueda encenderse y apagarse:

    • Un método virtual puro powerOn()
    • Un método virtual puro powerOff()
    • Un método virtual puro getPowerStatus() que devuelve un std::string
    • Un destructor virtual

    Connectable: cualquier dispositivo que pueda conectarse a una red:

    • Un método virtual puro connect(const std::string& network)
    • Un método virtual puro disconnect()
    • Un método virtual puro getConnectionInfo() que devuelve un std::string
    • Un destructor virtual
  • Devices.h: Implementa clases concretas de dispositivos que utilizan estas interfaces:

    Lamp: implementa únicamente Powerable:

    • Un miembro privado bool isOn (comienza como false)
    • Un miembro privado std::string name
    • Un constructor que recibe el nombre de la lámpara
    • powerOn() establece isOn en true e imprime: <name>: Light turned on
    • powerOff() establece isOn en false e imprime: <name>: Light turned off
    • getPowerStatus() devuelve "ON" u "OFF" según el estado

    SmartTV: implementa tanto Powerable como Connectable:

    • Miembros privados: bool isOn (comienza como false), std::string brand, std::string currentNetwork (comienza vacío)
    • Un constructor que recibe la marca del televisor
    • powerOn() establece isOn en true e imprime: <brand> TV: Powered on
    • powerOff() establece isOn en false, borra la red e imprime: <brand> TV: Powered off
    • getPowerStatus() devuelve "ON" u "OFF"
    • connect() almacena el nombre de la red e imprime: <brand> TV: Connected to <network>
    • disconnect() borra la red e imprime: <brand> TV: Disconnected
    • getConnectionInfo() devuelve "Connected to <network>" si está conectado, o "Not connected" si está vacío
  • main.cpp: Lee tres entradas (cada una en una línea independiente):
    1. Nombre de la lámpara
    2. Marca del televisor
    3. Nombre de la red

    Crea un Lamp y un SmartTV. Demuestra cómo se puede utilizar la misma interfaz con distintos dispositivos:

    Primero, trabaja con ambos dispositivos mediante la interfaz Powerable. Almacena punteros a ambos en un arreglo de Powerable*; después, recorre el arreglo y llama a powerOn() en cada uno, seguido de la impresión de su estado con el formato: Status: <powerStatus>

    Imprime una línea en blanco; después, trabaja con el SmartTV mediante la interfaz Connectable. Crea un puntero Connectable* a tu SmartTV, llama a connect() con el nombre de la red e imprime: Connection: <connectionInfo>

    Imprime otra línea en blanco; después, apaga ambos dispositivos mediante el arreglo Powerable y muestra su estado final.

Por ejemplo, con las entradas Desk Lamp, Samsung y 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

Observa cómo SmartTV puede tratarse como Powerable o como Connectable, según el puntero de interfaz que utilices. Esta flexibilidad es la ventaja de implementar varias interfaces. Tu código puede trabajar con cualquier dispositivo que cumpla el contrato que necesita, sin conocer el tipo concreto.

Pruébalo tú mismo

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

using namespace std;

int main() {
    // Lee las entradas
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: Crea un objeto Lamp y un SmartTV
    
    // TODO: Crea un array de punteros Powerable* que contenga ambos dispositivos
    
    // TODO: Recorre el array y llama a powerOn() en cada dispositivo
    // Después de cada powerOn(), imprime: Status: <powerStatus>
    
    // TODO: Imprime una línea en blanco
    
    // TODO: Crea un puntero Connectable* al SmartTV
    // Llama a connect() con el nombre de la red
    // Print: Connection: <connectionInfo>
    
    // TODO: Imprime una línea en blanco
    
    // TODO: Recorre el array Powerable y llama a powerOff() en cada dispositivo
    // Después de cada powerOff(), imprime: Status: <powerStatus>
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online