Menu
Coddy logo textTech

Interface Design in C++

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 61 of 104.

An interface in C++ is an abstract class where all member functions are pure virtual. Unlike abstract classes that may contain some implementation, interfaces define only what operations must exist - they specify a contract without any behavior.

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

This Drawable interface guarantees that any implementing class will have draw() and resize() methods, but says nothing about how they work. This separation is powerful - code can depend on the interface without knowing anything about the concrete types.

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 { /* resize button */ }
};

A class can implement multiple interfaces, enabling flexible designs where objects can fulfill different 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; }
};

Interfaces promote loose coupling - your code depends on abstractions rather than concrete implementations, making it easier to extend and test.

challenge icon

Challenge

Easy

Let's build a device management system that demonstrates how interfaces define contracts for different types of devices. You'll create two separate interfaces that devices can implement, then build concrete device classes that fulfill one or both of these contracts.

You'll organize your code across three files:

  • Interfaces.h: Define two pure abstract classes (interfaces) that represent different device capabilities:

    Powerable — any device that can be turned on and off:

    • A pure virtual powerOn() method
    • A pure virtual powerOff() method
    • A pure virtual getPowerStatus() method returning a std::string
    • A virtual destructor

    Connectable — any device that can connect to a network:

    • A pure virtual connect(const std::string& network) method
    • A pure virtual disconnect() method
    • A pure virtual getConnectionInfo() method returning a std::string
    • A virtual destructor
  • Devices.h: Implement concrete device classes that use these interfaces:

    Lamp — implements only Powerable:

    • A private bool isOn member (starts as false)
    • A private std::string name member
    • A constructor taking the lamp's name
    • powerOn() sets isOn to true and prints: <name>: Light turned on
    • powerOff() sets isOn to false and prints: <name>: Light turned off
    • getPowerStatus() returns "ON" or "OFF" based on state

    SmartTV — implements both Powerable and Connectable:

    • Private members: bool isOn (starts false), std::string brand, std::string currentNetwork (starts empty)
    • A constructor taking the TV's brand
    • powerOn() sets isOn to true and prints: <brand> TV: Powered on
    • powerOff() sets isOn to false, clears the network, and prints: <brand> TV: Powered off
    • getPowerStatus() returns "ON" or "OFF"
    • connect() stores the network name and prints: <brand> TV: Connected to <network>
    • disconnect() clears the network and prints: <brand> TV: Disconnected
    • getConnectionInfo() returns "Connected to <network>" if connected, or "Not connected" if empty
  • main.cpp: Read three inputs (each on a separate line):
    1. Lamp name
    2. TV brand
    3. Network name

    Create a Lamp and a SmartTV. Demonstrate how the same interface can be used with different devices:

    First, work with both devices through the Powerable interface. Store pointers to both in an array of Powerable*, then loop through and call powerOn() on each, followed by printing their status as: Status: <powerStatus>

    Print a blank line, then work with the SmartTV through the Connectable interface. Create a Connectable* pointer to your SmartTV, call connect() with the network name, and print: Connection: <connectionInfo>

    Print another blank line, then power off both devices through the Powerable array and show their final status.

For example, with inputs Desk Lamp, Samsung, and 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

Notice how the SmartTV can be treated as either a Powerable or a Connectable depending on which interface pointer you use. This flexibility is the power of implementing multiple interfaces — your code can work with any device that fulfills the contract it needs, without knowing the concrete type.

Cheat sheet

An interface in C++ is an abstract class where all member functions are pure virtual. Interfaces define only what operations must exist without any implementation:

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

Classes implement interfaces by overriding all pure virtual functions:

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

A class can implement multiple interfaces using multiple inheritance:

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 promote loose coupling by allowing code to depend on abstractions rather than concrete implementations.

Try it yourself

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

using namespace std;

int main() {
    // Read inputs
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: Create a Lamp and a SmartTV object
    
    // TODO: Create an array of Powerable* pointers containing both devices
    
    // TODO: Loop through the array and call powerOn() on each device
    // After each powerOn(), print: Status: <powerStatus>
    
    // TODO: Print a blank line
    
    // TODO: Create a Connectable* pointer to the SmartTV
    // Call connect() with the network name
    // Print: Connection: <connectionInfo>
    
    // TODO: Print a blank line
    
    // TODO: Loop through the Powerable array and call powerOff() on each device
    // After each powerOff(), print: Status: <powerStatus>
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming