面向对象编程的 SOLID 设计原则

【设计模式】SOLID设计原则 类应该只有一个单一的职责,即一个类应该有且只有一个改变的理由。这意味着一个类应该只负责一个特定的功能或任务,而不是多个不相关的功能。这样做可以提高类的内聚性,并使得类更容易理解、修改和测试。软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改现有代码的情况下,应该能够通过添加新的代码来扩展系统的功能。这样做可以使得系统更加稳定,减少修改现有代码可能带来的风险。子类型必须能够替换其基类型。换句话说,任何可以接受基类型的地方都可以接受子类型,而且不会引发意外的行为。 阅读详情

目录

背景

单一职责原则

开放封闭原则

里氏替换原则

接口隔离原则

依赖倒置原则

总结


背景

大家都知道C++是面向对象开发的,封装、继承与多态是面向对象开发的三大特征。相信大家都听说过设计模式,但SOLID设计原则不知道大家有没有了解过,这个问题面试也经常会问。

SOLID原则是五个面向对象设计原则的首字母缩写,它们旨在提高软件的可维护性和可扩展性。这些原则是由罗伯特·C·马丁(Robert C. Martin)在20世纪90年代提出的,分别是单一职责原则(S)、开放封闭原则(O)、里氏替换原则(L)、接口隔离原则(I)、依赖倒置原则(D)。


单一职责原则

单一职责原则(SRP - Single Responsibility Principle):一个类只负责一件事,应该只有一个引起它变化的原因。

例子:一个类应该只负责一个功能。

// 违反单一职责原则的类
class Logger {
public:
    void log(const std::string& message) {
        // 保存日志到文件
    }
    void sendAlert(const std::string& message) {
        // 发送警告邮件
    }
};

// 遵守单一职责原则的类
class FileLogger {
public:
    void log(const std::string& message) {
        // 保存日志到文件
    }
};

class AlertLogger {
public:
    void sendAlert(const std::string& message) {
        // 发送警告邮件
    }
};

开放封闭原则

开放封闭原则(OCP - Open/Closed Principle):类应该通过扩展而非修改来应对需求变化。软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。

例子:通过继承和多态实现扩展,而不是修改现有代码。

class Shape {
public:
    virtual double area() const = 0;
};

class Circle : public Shape {
public:
    double area() const override {
        return 3.14 * 4 * 4; // 假设半径为2
    }
};

class Square : public Shape {
public:
    double area() const override {
        return 4 * 4; // 边长为4
    }
};

// 新增形状时,只需添加新的类,无需修改现有代码
class Triangle : public Shape {
public:
    double area() const override {
        return 0.5 * 4 * 4; // 底和高都是4
    }
};

里氏替换原则

里氏替换原则(LSP - Liskov Substitution Principle):子类可以替代父类,不改变程序行为以及程序的正确性。

错误示例:

#include <iostream>

// 基类
class Rectangle {
public:
    virtual void setWidth(int width) {
        this->width = width;
    }

    virtual void setHeight(int height) {
        this->height = height;
    }

    virtual int getArea() const {
        return width * height;
    }

protected:
    int width;
    int height;
};

// 派生类
class Square : public Rectangle {
public:
    void setWidth(int width) override {
        this->width = height = width; // 保持宽高相等
    }

    void setHeight(int height) override {
        this->height = width = height; // 保持宽高相等
    }
};

void processShape(Rectangle& shape) {
    shape.setWidth(5);
    shape.setHeight(10);
    std::cout << "Expected area: 50, Got area: " << shape.getArea() << std::endl;
}

int main() {
    Rectangle rect;
    Square sqr;

    processShape(rect); // 处理矩形
    processShape(sqr);  // 处理正方形

    return 0;
}

在这个例子中,Square 类是从 Rectangle 类派生的。根据LSP,任何父类 (Rectangle) 的对象都可以被其子类 (Square) 的对象替换,而不改变程序的预期行为。在 processShape 函数中,我们期望无论传入的是 Rectangle 还是 Square 对象,理论应该都能正确计算面积。

然而,由于 Square 类重写了 setWidth 和 setHeight 方法,使得设置宽度和高度时会相互影响,这违反了LSP原则。因此,尽管 Square 是 Rectangle 的子类型,但它们的行为并不完全一致,导致在使用 Square 对象时,程序的行为与预期不符。

正确的示例应该创建一个更通用的 Shape 基类,让 Rectangle 和 Square 都继承自这个基类去遵循里氏替换原则。


接口隔离原则

接口隔离原则(ISP - Interface Segregation Principle):不应强制客户端依赖于它们不使用的接口,接口应该小而精,避免无关功能。

这个原则很好理解,简单解释一下,现在有两个客户user1和user2,对于user1而言,他会用到所有OPS的接口,对于user2,这个客户只需要用OPS里面的op1和op2这两个接口,别的都用不到,这时候开发人员只需要在中间加一个IUOps隔离开然后交付给user2就可以了。

错误示例:

// 不合理的接口,违反了接口隔离原则
class IAnimal {
public:
    virtual void eat() = 0;
    virtual void fly() = 0;
};
// 鸟类实现了吃和飞
class Bird : public IAnimal {
public:
    void eat() override {
        std::cout << "Bird is eating." << std::endl;
    }
    void fly() override {
        std::cout << "Bird is flying." << std::endl;
    }
};

// 鱼类只实现了吃,没有实现飞
class Fish : public IAnimal {
public:
    void eat() override {
        std::cout << "Fish is eating." << std::endl;
    }
    void fly() override {
        // 鱼类不需要飞,这里可以抛出异常或者不实现该方法
        std::cout << "Fish cannot fly." << std::endl;
    }
};

以上代码由于Fish类不需要fly方法,我们可以认为IAnimal接口违反了接口隔离原则。为了解决这个问题,我们可以将IAnimal接口拆分成两个更小的接口:

正确代码:

// 将IAnimal拆分成两个接口
class IEat {
public:
    virtual void eat() = 0;
};

class IFly {
public:
    virtual void fly() = 0;
};
// 鸟类实现了吃和飞
class Bird : public IEat, public IFly {
public:
    void eat() override {
        std::cout << "Bird is eating." << std::endl;
    }
    void fly() override {
        std::cout << "Bird is flying." << std::endl;
    }
};

// 鱼类只实现了吃
class Fish : public IEat {
public:
    void eat() override {
        std::cout << "Fish is eating." << std::endl;
    }
};

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

  • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。


依赖倒置原则

依赖倒置原则(DIP - Dependency Inversion Principle):高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

例子:通过抽象(接口或抽象类)来实现模块间的依赖关系。

#include <iostream>

// 定义一个抽象的日志记录器接口
class ILogger {
public:
    virtual ~ILogger() {}
    virtual void Log(const std::string& message) = 0;
};

// 实现具体的控制台日志记录器
class ConsoleLogger : public ILogger {
public:
    void Log(const std::string& message) override {
        std::cout << "Console Logger: " << message << std::endl;
    }
};

// 实现具体的文件日志记录器
class FileLogger : public ILogger {
public:
    void Log(const std::string& message) override {
        // 假设这里写入到文件,实际中需要打开文件等操作
        std::cout << "File Logger: " << message << std::endl;
    }
};

// 应用程序类,依赖于日志记录器的接口而不是具体实现
class Application {
private:
    ILogger* logger; // 使用接口指针来引用日志记录器
public:
    Application(ILogger* logger) : logger(logger) {}
    void Run() {
        // 业务逻辑...
        logger->Log("Application is running.");
    }
};

int main() {
    // 创建具体的日志记录器实例
    ConsoleLogger consoleLogger;
    FileLogger fileLogger;

    // 将具体的日志记录器注入到应用程序中
    Application app1(&consoleLogger);
    app1.Run();

    Application app2(&fileLogger);
    app2.Run();

    return 0;
}

在这个示例中,ILogger是一个抽象的日志记录器接口,它定义了一个Log方法。ConsoleLogger和FileLogger是这个接口的两个具体实现,分别用于在控制台和文件中记录日志。Application类依赖于ILogger接口,而不是具体的实现,这符合依赖倒置原则。通过这种方式,我们可以很容易地替换或扩展日志记录方式,而无需修改Application类的代码。


总结

1.单一职责原则(SRP - Single Responsibility Principle):一个类只负责一件事。
2.开放封闭原则(OCP - Open/Closed Principle):类应该通过扩展而非修改来应对需求变化。
3.里氏替换原则(LSP - Liskov Substitution Principle):子类可以替代父类,不改变程序行为。
4.接口隔离原则(ISP - Interface Segregation Principle):不应强制客户端依赖于它们不使用的接口,接口应该小而精,避免无关功能。
5.依赖倒置原则(DIP - Dependency Inversion Principle):高层模块依赖于抽象而非具体实现。

最后,如果觉得我的文章对你有帮助,请点赞收藏加关注,谢谢大家。

比亚迪开放平台接口实战:车身状态监测与智能车联应用开发 本文详细介绍了如何利用比亚迪开放平台的车身状态接口进行智能车联应用开发。通过核心类BYDAutoBodyworkDevice,开发者可以安全获取车辆VIN、车门车窗状态、蓄电池电压、方向盘转角等实时数据,并利用监听器实现状态实时响应。文章提供了从SDK集成、接口调用到典型应用场景(如远程车况检查、车队管理)的完整实战指南,助力开发者快速构建基于比亚迪车辆数据的创新应用。 阅读详情

相关推荐

MCP详解:手把手教你理解和如何使用各种开源MCP服务

MCP 作为一种开放协议,为 AI 模型与外部世界的连接提供了标准化的解决方案。它不仅降低了开发门槛,还极大地扩展了 AI 应用的边界。无论是开发者还是普通用户,都可以通过 MCP 更高效地利用 AI 技术,实现更多智能化的功能。

王哥儿聊AI的博客 6054

设计原则——SOLID

设计原则SOLID

qq_40843987的博客 5356

攻防世界 Crypto 新手练习区 1-12 全详解

本篇是攻防世界 Crypto 新手练习区的全解

闵行小鱼塘 3732

每个开发都要掌握的 SOLID 五大设计原则

单一职责原则(Single responsibility)开闭原则(Open-closed)里氏替换原则(Liskov substitution)接口隔离原则(Interface segregation)依赖倒置原则(Dependency inversion)深入理解这些概念可以帮助我们编写更易于理解、灵活且易于维护的代码。

NVG_Haru的博客 2964

生态位宽度计算&可视化展示(R语言)

物种的生态位越宽,该物种的特化程度就越小,倾向于泛化种(generalist species);物种的生态位越窄,倾向于是一个特化种(specialists species)。生态位宽度是指物种(或其它生物单位)在群落中所利用的各种不同资源的总和。本篇所使用为生态位宽度指数即**Levins的生态位宽度指数。

weixin_53705272的博客 4435

保姆级教程:用Labelme标注车辆关键点,并一键转成YOLOv8-pose训练格式

本文提供了一份详细的车辆底盘关键点标注教程,从使用Labelme进行标注到一键转换为YOLOv8-pose训练格式的全流程指南。特别针对工业检测和自动驾驶场景,分享了标注规范、格式转换脚本及常见问题解决方案,帮助开发者高效准备训练数据并优化模型性能。

weixin_27419879的博客 306

SOLID 设计原则 (有点长但很透彻)

写了这么多年代码,你真的了解 SOLID 设计原则吗?本文试图通过最通俗的语言、最典型的示例,带你了解其中的奥秘,并且把它刻进你的脑子里!

Ethan_LG的专栏 1871

易灵思Ti60F100 FPGA的HyperRAM实战:从硬件连接到Native接口时序调试

本文详细介绍了易灵思Ti60F100 FPGA内置HyperRAM控制器的实战应用。文章从硬件设计要点(如400MHz+信号完整性、等长匹配)切入,重点解析了如何通过Interface Designer配置高效的Native接口,并分享了其时序详解与调试技巧,为开发者提供了一份从硬件连接到软件调试的深度指南。

weixin_29168153的博客 454

【设计模式】SOLID 设计原则概述

SOLID 是面向对象设计中的五大原则,不管什么面向对象的语言, 这个准则都很重要,如果你没听说过,赶紧先学一下。原则可以写出更好的代码,使系统更易扩展、维护和测试。在设计类和模块时,应尽量。,使代码更加健壮、易于测试和扩展。这样每个类的变更都不会影响其他功能,符合 SRP。,否则后期修改代码时,可能会影响其他无关功能。,避免不必要的修改。

qq_31638535的博客 1337

Tessent --- PDL

当关联的ICL模块是当前ICL顶部模块时,即当它与当前设计匹配时,只能iCall一个iTopProc。但是,可以在iTopProc内调用iCall iProc或iTopProc。为指定后续iProc命令引用的ICL模块,发出这个命令之后,随后定义的iProc都会对应的ICL模块进行定义。为后续iCall调用,当使用iCall命令调用该过程时,参数列表中的变量将被替换为iCall命令中指定的值。在调用iProc时,运行iProc内的命令。PDL是面向工程师的,工程师利用PDL语言,对ICL中的单元进行控制。

qiantianyiqian的博客 2783

华为OD机试真题 新系统 2026-05-13 Python&JS语言 实现【查找能被整除的最大整数】

给定一个字符串和一个正整数,字符串由大小写字母和数字组成,要求从字符串中找出最大且能被给定正整数整除的数。

MISAYAONE的博客 224

一文漫谈面向对象编程中的SOLID设计原则

单一职责原则(Single Responsibility Principle, SRP)是最基础、最核心的原则之一。单一职责原则(SRP)要求一个类只承担单一职责,即只有一个引起它变化的原因。例如:• ❌ 错误示范:UserService同时处理用户数据、数据库操作和邮件通知• ✅ 正确做法:拆分为User(数据)、UserRepository(数据库)、EmailService(通知)开闭原则是构建抗脆弱系统的核心准则,通过:抽象隔离变化点多态实现动态扩展依赖倒置降低耦合。

andy dennis的博客 981

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

面向对象编程(OOP)的 SOLID 设计原则

我们希望遵循 OCP,在添加新的形状类型的同时,而不修改已有的代码,关键技术在于使用。一个类不应该强迫它的客户端依赖于它们不需要使用的方法,即:只提供必要的接口。ISP 要求我们不应设计 " 胖 " 接口," 胖 " 接口会导致不够聚合。此时,当再次面对变化时,例如增加一个照明设备,我们只需要新增一个类,实现。在上述示例中,如果需要新增一种形状,如三角形,那么需要修改。中实现,会导致类的职责不清晰,增加了类的复杂性和耦合性。的相关方法,改动分支语句,较为繁琐。),还需要控制 LED,白炽灯等。

Megumin的博客 908

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

六大设计原则 面向接口面向对象编程 SOLID原则

接口不应该强迫实现类依赖它们不需要的方法,即一个接口应该只包含客户端所需要的方法。高层模块不应该依赖低层模块,它们都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。子类对象应该可以替换父类对象而不会导致程序出错。软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。一个类应该只与直接的朋友类交互,而不应该直接调用其他类的细节。基本原则最小化类之间的依赖:一个类不应直接与另一个类的内部细节打交道,尽量减少调用对象的方法链。

weixin_43791406的博客 1257

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

labview圆环里实物动画图形_LabVIEW面向对象编程_初窥门槛(2)_设计原则(SOLID)...

该系列一共是9篇文章,本文是该系列的第二篇,是讲述通用的面向对象编程设计原则(SOLID),LabVIEW作为一门具备面向对象编程范式的图形化编程语言当然也要遵循上述的通用设计原则,当然除了接口隔离原则无法直接套用外。本系列的首篇文章提到了在学习LabVIEW的面向对象使用时,往往都会先去看看原厂的随机帮助系统和样例,但是这些只是提供基本概念(封装、继承、多态)及如何去做(How To)的操作细节...

weixin_39744384的博客 225

面向对象编程实战:从类、封装、继承到SOLID设计原则的工程落地

面向对象编程(OOP)是现代软件工程的核心思想之一,它通过类与对象将现实业务抽象为可维护的代码结构。其三大核心机制——封装、继承、多态——分别解决了变化点隔离、代码复用与统一调用的难题,而SOLID设计原则则进一步为大型项目的扩展性与稳定性提供了检验标准。在实际开发中,类的粒度控制、接口的灵活设计、组合优于继承的取舍,以及深拷贝与equals/hashCode等细节,往往决定系统能否长期健康演进。本文从基础概念出发,结合订单系统的完整案例,对比Java与Python的OOP实现差异,并给出常见陷阱的排查技巧

weixin_29214691的博客 289

SOLID设计原则

设计原则

hamlyli的博客 1058

面向对象编程OOP深度解析:从核心特性到SOLID设计原则的工程实践指南

面向对象编程(OOP)是现代软件工程中组织代码、控制复杂度的核心范式。它通过封装、继承、多态与抽象,将数据和行为聚合为自治对象,模拟真实世界的协作方式,从而解决面向过程编程中代码与数据割裂的痛点。理解OOP不能仅停留在语法层面,更需掌握其背后的设计思想。SOLID原则(如单一职责、开闭原则、依赖倒置)为开发者提供了从“会语法”到“会设计”的实践路径,帮助构建高内聚、低耦合的系统。在Web开发、微服务等复杂业务场景中,合理运用面向对象分析与设计模式,结合接口编程与组合优于继承的理念,能显著提升代码的可测试性、

weixin_33824385的博客 292

理解SOLID on Programming

SOLID In object-oriented computer programming, SOLID is a mnemonic acronym for five design principles to make software designs more understandable, flexible, and maintainable. Although they apply to any object-oriented design, the SOLID principles can al..

漫步量化 442

学霸带你游戏化 C# 面向对象、设计原则及模式

理解 OOP 的定义是学习的第一步。面向对象的理念帮助开发者更好地组织代码,形成清晰的逻辑结构。OOP(面向对象编程)是一种以对象为基础的编程范式,通过类定义和实例化对象来组织代码。在《最终幻想 15》(Final Fantasy XV)中,角色和敌人被视作对象。参考游戏:选择《最终幻想 15》,因为其角色和系统设计展示了 OOP 的优势。示例代码set;set;优化建议if (health < 0) throw new ArgumentException("生命值不能为负");

纽雪澳诺加海美德的博客 1316

【转载】SOLID 设计原则

转自:https://www.cnblogs.com/huangenai/p/6219475.html SOLID 设计原则 SOLID 原则基本概念: 程序设计领域, SOLID (单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期 引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。当这些原则被一起...

a18792721831的博客 329

面向对象设计原则

[b]5个基本原则SOLID[/b] S SRP Single responsibility principle: an object should have only a single responsibility. O OCP Open/closed principle: “software entities … should be open fo...

Inner peace 128

设计模式重构代码_面向对象、设计原则、设计模式、编程规范、重构,别傻傻分不清...

面向对象现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程、面向对象和函数式编程。面向对象这种编程风格又是这其中最主流的。现在比较流行的编程语言大部分都是面向对象编程语言。大部分项目也都是基于面向对象编程风格开发的。面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式编码实现的基础。面向对象的四大特性:封装、抽象、继承、多态面向...

weixin_39543478的博客 207

OO设计原则SOLID之浅分析

OO的五大设计原则 (SRP) The Single Responsibility Principle 单一责任原则(OCP) The Open-Closed Principle 开放封闭原则(LSP) The Liskov Substitution Principle Liskov替换原则(DIP) The Dependency Inversion Principle 依赖转置原则(ISP

u012665701的专栏 979

面向对象、设计原则、设计模式、编程规范、重构

面向对象、设计原则、设计模式、编程规范、重构 面向对象 主流的三个编程风格有:面向对象,面向过程,函数式编程。 面向对象是最主流的风格,面向对象具有丰富的特性(封装,抽象,继承,多态)。 面向对象 面向对象的4大特性:封装、继承,抽象、多态。 面向对象编程与面向过程的区别和联系 面向对象分析,面向对象设计、面向对象编程 接口和抽象类的区别与各自的应用场景 基于接口实现的编程的设计思想 多个组合少用继承的设计思想 多面向过程的贫血模型和面向对象的充血模型 设计原则 设计原则是指导我们代码设计的一些总结,设

王小明的专栏 514

举例说明层次分析的三大原则_LabVIEW面向对象编程_初窥门槛(2)_设计原则(SOLID)...

该系列一共是9篇文章,本文是该系列的第二篇,是讲述通用的面向对象编程设计原则(SOLID),LabVIEW作为一门具备面向对象编程范式的图形化编程语言当然也要遵循上述的通用设计原则,当然除了接口隔离原则无法直接套用外。本系列的首篇文章提到了在学习LabVIEW的面向对象使用时,往往都会先去看看原厂的随机帮助系统和样例,但是这些只是提供基本概念(封装、继承、多态)及如何去做(How To)的操作细节...

weixin_39688870的博客 383
上一篇: Unicode、ASCII、UTF-8编码关系和底层原理
下一篇: 静态链接和动态链接
CodeCheers
博客等级 码龄5年 152粉丝 15原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值