当前位置: 首页 > article >正文

OO设计原则的cpp举例

下面我们详细介绍常见的面向对象设计原则,并用 C++ 举例说明如何在代码中体现这些原则。


1. 单一职责原则(SRP)

原则含义:
一个类应该只有一个引起它变化的原因,也就是说一个类只负责一项职责。这样可以降低类的复杂度,提高内聚性,方便维护和复用。

举例说明:
假设在一个学生管理系统中,我们可能会将学生的业务操作(增删改查)和数据存储操作混合在同一个类中,这样类的职责不单一。遵循 SRP 后,我们可以拆分成两个类:一个专注于学生业务操作,一个专注于数据的读写。

C++ 示例:

// 负责学生的业务操作
class StudentManager {
public:
    void addStudent(const std::string& name) {
        // 添加学生的业务逻辑
    }
    void removeStudent(const std::string& name) {
        // 删除学生的业务逻辑
    }
    // …其他学生操作
};

// 负责学生数据的存取
class StudentRepository {
public:
    void save(const std::vector<std::string>& students) {
        // 将学生数据保存到文件或数据库中
    }
    std::vector<std::string> load() {
        // 从文件或数据库中加载学生数据
        return {};
    }
};

2. 开放-封闭原则(OCP)

原则含义:
软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。也就是说,当需求变化时,我们应该通过扩展代码而不是修改已有代码来实现新功能。

举例说明:
以图形绘制为例,我们设计一个基类 Shape,提供一个虚函数 draw()。新增图形时,只需继承 Shape 并实现自己的 draw() 方法,而无需修改绘图函数。

C++ 示例:

// 抽象基类:图形
class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() {}
};

// 圆形实现
class Circle : public Shape {
public:
    void draw() const override {
        // 绘制圆形的具体逻辑
        std::cout << "Drawing Circle" << std::endl;
    }
};

// 矩形实现
class Rectangle : public Shape {
public:
    void draw() const override {
        // 绘制矩形的具体逻辑
        std::cout << "Drawing Rectangle" << std::endl;
    }
};

// 绘图函数,只依赖于抽象接口
void renderShape(const Shape& shape) {
    shape.draw();
}

int main() {
    Circle circle;
    Rectangle rectangle;
    renderShape(circle);
    renderShape(rectangle);
    return 0;
}

3. 里氏替换原则(LSP)

原则含义:
子类对象必须能够替换基类对象而不会破坏程序的正确性,即任何使用基类的地方,都可以用子类来代替,而程序行为不变。

常见问题:
经典例子是正方形与长方形的问题。如果将正方形直接继承自长方形,由于正方形要求长宽相等,可能导致在设置长和宽时产生不一致,从而违反 LSP。

C++ 示例:

// 基类:长方形
class Rectangle {
protected:
    double width;
    double height;
public:
    virtual void setWidth(double w) { width = w; }
    virtual void setHeight(double h) { height = h; }
    virtual double area() const { return width * height; }
    virtual ~Rectangle() {}
};

// 正方形不宜简单地继承自 Rectangle
// 因为 setWidth 与 setHeight 可能导致状态不一致
class Square : public Rectangle {
public:
    // 重写设置方法,保证宽高始终相等
    void setWidth(double w) override {
        width = height = w;
    }
    void setHeight(double h) override {
        width = height = h;
    }
    // area() 方法可以直接继承,但注意:在设计时应考虑是否真的适合用继承来描述正方形与长方形的关系
};

void processRectangle(Rectangle& rect) {
    rect.setWidth(5);
    rect.setHeight(4);
    // 对于 Rectangle,面积应为20
    std::cout << "Area: " << rect.area() << std::endl;
}

int main() {
    Rectangle r;
    r.setWidth(5);
    r.setHeight(4);
    processRectangle(r); // 输出 20

    Square s;
    // 如果用 Square 替换 Rectangle,调用过程会修改为正方形的行为
    processRectangle(s); // 输出可能不是 20,而是 16,违反 LSP
    return 0;
}

说明: 正方形与长方形之间的继承关系容易引起混淆,设计时可以考虑采用组合而不是继承来避免问题。


4. 接口隔离原则(ISP)

原则含义:
客户端不应被迫依赖它不需要的接口。一个“胖”接口应拆分为多个更小、更具体的接口,使得每个客户端只依赖于自己感兴趣的方法。

举例说明:
假设有一个多功能设备接口包含打印、扫描和传真方法,但某些客户端只需要打印功能。可以将接口拆分为 IPrinterIScannerIFax 等。

C++ 示例:

// 分离接口
class IPrinter {
public:
    virtual void print(const std::string& document) = 0;
    virtual ~IPrinter() {}
};

class IScanner {
public:
    virtual void scan(const std::string& document) = 0;
    virtual ~IScanner() {}
};

class IFax {
public:
    virtual void fax(const std::string& document) = 0;
    virtual ~IFax() {}
};

// 具体设备实现了打印和扫描,不支持传真
class MultiFunctionPrinter : public IPrinter, public IScanner {
public:
    void print(const std::string& document) override {
        std::cout << "Printing: " << document << std::endl;
    }
    void scan(const std::string& document) override {
        std::cout << "Scanning: " << document << std::endl;
    }
};

void clientPrint(IPrinter* printer) {
    printer->print("Test Document");
}

int main() {
    MultiFunctionPrinter mfp;
    clientPrint(&mfp);
    return 0;
}

5. 依赖倒置原则(DIP)

原则含义:
高层模块不应依赖于低层模块,二者都应依赖于抽象;抽象不应依赖于细节,细节应依赖于抽象。这使得系统更容易扩展和维护。

举例说明:
例如在支付系统中,高层模块(订单)只依赖于支付接口,而具体的支付方式(支付宝、微信支付等)则实现该接口,高层模块无需修改就可以支持更多支付方式。

C++ 示例:

// 抽象接口
class Payment {
public:
    virtual void pay(double amount) = 0;
    virtual ~Payment() {}
};

// 具体支付方式:支付宝
class Alipay : public Payment {
public:
    void pay(double amount) override {
        std::cout << "Paying " << amount << " using Alipay." << std::endl;
    }
};

// 具体支付方式:微信支付
class WechatPay : public Payment {
public:
    void pay(double amount) override {
        std::cout << "Paying " << amount << " using WechatPay." << std::endl;
    }
};

// 高层模块:订单
class Order {
private:
    Payment* paymentMethod;
public:
    // 通过依赖注入设置支付方式
    void setPaymentMethod(Payment* method) {
        paymentMethod = method;
    }
    void processOrder(double amount) {
        if(paymentMethod)
            paymentMethod->pay(amount);
    }
};

int main() {
    Order order;
    Alipay alipay;
    order.setPaymentMethod(&alipay);
    order.processOrder(100.0);

    WechatPay wechat;
    order.setPaymentMethod(&wechat);
    order.processOrder(200.0);
    return 0;
}

小结

这五大原则(即 SOLID 原则)分别强调:

  • SRP 保证类职责单一;
  • OCP 让系统能够在不修改已有代码的情况下扩展新功能;
  • LSP 保证子类能够替换父类;
  • ISP 让接口足够细化,只暴露客户端所需的方法;
  • DIP 通过依赖抽象降低高层与低层模块之间的耦合性。

这些原则是面向对象设计中的核心思想,能够帮助我们构建出易于维护、扩展和复用的软件系统。


参考资料:
citeturn0search0、citeturn0search9

以上就是 OO 设计原则的详细介绍及 C++ 实例,希望对你理解和应用面向对象设计有所帮助。


http://www.kler.cn/a/559935.html

相关文章:

  • langchain4j+springboot流式输出
  • 深度解析SmartGBD助力Android音视频数据接入GB28181平台
  • 【工具篇】【深度解析字节跳动AI编程工具Trae:中文开发者的新利器】
  • git中的merge和rebase的区别
  • XGBoost 入门介绍
  • SPV技术前沿:高分辨率表面光电压测量-测试狗
  • 日本IT|SQL工程师的工作内容以及所需的技能·资格
  • Spring Boot 2/3.x 中 MultipartFile 接收问题深度解析与实战解决方案
  • C++ 继承与运算符重载的简单练习
  • 爱迪斯通虚拟针灸教学系统入选ARinChina2024 XR行业年度荣誉榜医疗榜单
  • BOOST电路设计
  • 20250212:https通信
  • Directed acyclic graph [DAG]有向无环图 应用场景汇总与知名开源库实现细节说明
  • 【Go】Go wire 依赖注入
  • 面试八股文--数据库基础知识总结(1)
  • 深度学习pytorch之19种优化算法(optimizer)解析
  • 模块化设计的力量:从「乐高积木」看组合式开发如何降低软件工程风险
  • 数据同步的中间件
  • 每日学习Java之一万个为什么
  • 【K8s】专题十六(2):Kubernetes 包管理工具之 Helm 使用