Menu
CoddyTech

C++ 中的接口设计

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 61 课,共 104 课。

C++ 中的接口是一种抽象类,其中每个成员函数(虚析构函数除外)都是纯虚函数。与可能包含部分实现的抽象类不同,接口只定义必须存在的操作,它们规定了一种契约,但不包含任何行为。

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

这个 Drawable 接口可以确保任何实现它的类都具有 draw() 和 resize() 方法,但并未说明它们如何工作。这种分离很有用:代码可以依赖该接口,而无需了解具体类型的任何信息。

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 { /* 调整按钮大小 */ }
};

一个类可以实现多个接口,从而支持灵活的设计,让对象能够扮演不同的角色:

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

接口有助于实现松耦合:你的代码依赖抽象,而不是具体实现,因此更容易扩展和测试。

challenge icon

挑战

简单

我们来构建一个设备管理系统,演示接口如何为不同类型的设备定义契约。你将创建两个设备可以实现的独立接口,然后构建满足其中一个或两个契约的具体设备类。

你将把代码分成 3 个文件:

  • Interfaces.h:定义两个表示不同设备能力的纯抽象类(接口):

    Powerable:任何可以开关机的设备:

    • 一个纯虚方法 powerOn()
    • 一个纯虚方法 powerOff()
    • 一个纯虚方法 getPowerStatus(),返回 std::string
    • 一个虚析构函数

    Connectable:任何可以连接到网络的设备:

    • 一个纯虚方法 connect(const std::string& network)
    • 一个纯虚方法 disconnect()
    • 一个纯虚方法 getConnectionInfo(),返回 std::string
    • 一个虚析构函数
  • Devices.h:实现使用这些接口的具体设备类:

    Lamp:只实现 Powerable:

    • 一个私有成员 bool isOn(初始值为 false)
    • 一个私有成员 std::string name
    • 一个接收灯具名称的构造函数
    • powerOn() 将 isOn 设为 true,并打印:<name>: Light turned on
    • powerOff() 将 isOn 设为 false,并打印:<name>: Light turned off
    • getPowerStatus() 根据状态返回 "ON" 或 "OFF"

    SmartTV:同时实现 Powerable 和 Connectable:

    • 私有成员:bool isOn(初始值为 false)、std::string brand、std::string currentNetwork(初始值为空)
    • 一个接收电视品牌的构造函数
    • powerOn() 将 isOn 设为 true,并打印:<brand> TV: Powered on
    • powerOff() 将 isOn 设为 false,清空网络,并打印:<brand> TV: Powered off
    • getPowerStatus() 返回 "ON" 或 "OFF"
    • connect() 保存网络名称,并打印:<brand> TV: Connected to <network>
    • disconnect() 清空网络,并打印:<brand> TV: Disconnected
    • 如果已连接,getConnectionInfo() 返回 "Connected to <network>";如果网络为空,则返回 "Not connected"
  • main.cpp:读取 3 个输入(每个输入各占一行):
    1. 灯具名称
    2. 电视品牌
    3. 网络名称

    创建一个 Lamp 和一个 SmartTV。演示如何对不同设备使用同一个接口:

    首先,通过 Powerable 接口操作这两个设备。将它们的指针存入一个 Powerable* 数组,然后循环遍历数组,对每个设备调用 powerOn(),接着按以下格式打印它们的状态:Status: <powerStatus>

    打印一个空行,然后通过 Connectable 接口操作 SmartTV。创建一个指向该 SmartTV 的 Connectable* 指针,使用网络名称调用 connect(),并打印:Connection: <connectionInfo>

    再打印一个空行,然后通过 Powerable 数组关闭这两个设备,并显示它们的最终状态。

例如,输入 Desk Lamp、Samsung 和 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

请注意,根据你使用的接口指针,SmartTV 可以被视为 Powerable 或 Connectable。这正是实现多个接口所带来的灵活性。你的代码可以与任何满足所需契约的设备协作,而不必知道它的具体类型。

亲手试试

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

using namespace std;

int main() {
    // 读取输入
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO:创建一个 Lamp 对象和一个 SmartTV 对象
    
    // TODO:创建一个包含这两个设备的 Powerable* 指针数组
    
    // TODO:遍历数组,对每个设备调用 powerOn()
    // 每次调用 powerOn() 后,打印:Status: <powerStatus>
    
    // TODO:打印一个空行
    
    // TODO:创建一个指向 SmartTV 的 Connectable* 指针
    // 用网络名称调用 connect()
    // 打印:Connection: <connectionInfo>
    
    // TODO:打印一个空行
    
    // TODO:遍历 Powerable 数组,对每个设备调用 powerOff()
    // 每次调用 powerOff() 后,打印:Status: <powerStatus>
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器