面向对象设计五大原则(SOLID原则) 2025-10-02

(solid中文翻译是固体) solid是五种准则的首字母组成的

(主要说的是我自己)
我们一方面或在课本上或专门学习了SOLID原则,
另一方面在写代码的时候却不会从这些原则的角度出发,而是写完了代码才去比对这份代码用了什么、遵循了什么。

导致了一种割裂感,这份代码可能也不会是一份非常好的代码,是缺乏思考的;
与此同时也没有对这些原则有很深的理解,常常忘是很自然的。

因此我想要加强理解,最好能从代码的角度去看设计模式。


原则名称 核心思想 主要目标
S - 单一职责原则 (SRP) 一个类应该有且仅有一个引起它变化的原因。 降低复杂度,提高内聚性。
O - 开放封闭原则 (OCP) 软件实体应对扩展开放,对修改关闭。 提升系统的稳定性和可扩展性。
L - 里氏替换原则 (LSP) 子类必须能够替换其父类,且程序行为不变。 确保继承关系的正确性。
I - 接口隔离原则 (ISP) 客户端不应被迫依赖它不使用的接口。 减少不必要的耦合。
D - 依赖倒置原则 (DIP) 高层模块不应依赖低层模块,二者都应依赖抽象。 降低模块间的耦合度,提高灵活性。

单一职责原则

单一职责原则:一个类应该仅有一个引起它变化的原因
我之前在使用的时候秉称的就是每一个类只有一个功能,但不够准确
这里面的高内聚:单一职责原则鼓励我们将因相同原因而变化的元素聚集在一起,将因不同原因而变化的元素分离开。最终:类内部联系紧密(高内聚),与其他类的依赖关系简单(低耦合)。
实际的时候要灵活处置,感觉还是要写的代码看的代码够多,处理的场景够多
总结:单一职责原则:一个类应该仅有一个引起它变化的原因、修改一个功能时是否会影响其他模块、该类的功能能否很清晰的就描述出?

一个类应该仅有一个引起它变化的原因:
在设计或评审一个类时,问自己:“这个类未来可能会有哪些不同的理由导致它需要被修改?”如果答案多于一个,那么这个类就可能承担了多个职责,需要考虑拆分。例如,一个类如果既负责订单的计算逻辑,又负责订单数据的数据库存储,那么计算规则变化和数据库类型变化都会导致修改这个类,这就违反了SRP。


#include <iostream>

class OrderItem
{
    public:
        double getPrice() const {return price;}
        int getQuantity() const {return quantity;}
    private:
        double price;
        int quantity;
};

class Order
{
    public:
        std::vector<OrderItem> getItems() const {return items;}
        std::string getOrderId() const {return orderId;}
    private:
        std::vector<OrderItem> items;
        std::string orderId;
};

//模拟库存服务
class StockService
{
    public:
        static void deductStock(const std::string& productId,int quantity)
        {
            std::cout << "Deducting stock for product: " << productId 
                  << ", quantity: " << quantity << std::endl;
        }
};

//订单服务类, 没有遵循单一职责原则
class OrderService
{
    //计算订单价格,包含促销规则和运费
    double calculateOrderPrice(const Order& order)
    {
        double totalPrice = 0.0;
        //遍历订单商品
        for(const auto& item:order.getItems())
        {
            totalPrice+=item.getPrice()*item.getQuantity();
        }

        // 应用促销规则:满100减10
        if (totalPrice >= 100) {
            totalPrice -= 10;
        }
        
        // 计算固定运费
        totalPrice += 10;
        return totalPrice;
    }

    //扣减库存
    void deductStock(const Order& order)
    {
        for(const auto& item:order.getItems())
        {
            //调用静态库存服务
            StockService::deductStock(item.getProductId(), item.getQuantity());
        }
    }
    // 记录订单日志(对应Java的logOrder方法)
    void logOrder(const Order& order) {
        // C++中可使用标准输出或日志库(如spdlog),此处使用std::cout模拟
        std::cout << "Order created: " << order.getOrderId() << std::endl;
    }
};

按照单一职责原则进行重构

//订单计算类
class OrderCalculator
{
    double calculateOrderPrice(const Order& order)
    {
        double totalPrice = 0;
        for(OrderItem item:order.getItems())
        {
            totalPrice +=item.getPrice()*item.getQuantity();
        }
        if(totalPrice>=100)
        {
            totalPrice-=10;
        }
        totalPrice+=10;
        return totalPrice;
    }
};

//库存管理类
class StockManager
{
    void deductStock(const Order& order)
    {
        for(OrderItem item:order.getItems())
        {
            StockService.deductStock(item.getProductId(),item.getQuantity());
        }
    }
};

class OrderLogger
{
      void logOrder(const Order& order) {
        // C++中可使用标准输出或日志库(如spdlog),此处使用std::cout模拟
        std::cout << "Order created: " << order.getOrderId() << std::endl;
    }
};

开闭原则(OCP)

开闭原则的核心思想:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

当软件系统需要添加新功能或应对需求变化时,应该通过扩展已有代码的方式来实现,
而不是修改已有的、经过测试且稳定运行的代码。

遵循开闭原则可以有效提高软件的可维护性和可扩展性,降低修改代码带来的风险 。
订单系统
上面这个链接中举了一个例子,是这样描述的

假设最初系统只支持信用卡支付,随着业务的发展,需要增加支付宝和微信支付。如果不遵循开闭原则,直接在订单支付类中添加支付宝和微信支付的逻辑,代码会变得越来越臃肿,维护难度也会大幅增加。而且,每次修改都可能引入新的问题,影响系统的稳定性。

解决就是策略模式的应用

里氏替换原则:

核心思想:
继承父类的子类应该是对父类功能的扩展,而非改变。

接口隔离原则:接口按需实现

依赖倒置原则:

核心思想是:高层模块不应该依赖低层模块的具体实现,而是依赖于抽象接口 ;抽象不应该依赖细节,细节应该依赖于抽象 。在软件系统中,高层模块通常负责实现核心业务逻辑,而低层模块负责提供基础的原子操作。
目的是当低层模块发生变化时,高层模块不需要进行修改。
要做的是:高层模块实现功能、低层模块实现抽象(抽象接口或抽象类)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容