面向对象设计:SOLID 原则


C++ 面向对象设计原则详解:SOLID 原则与实践示例

摘要:面向对象设计原则(SOLID)是构建高质量、可维护代码的基石。本文通过 C++ 代码示例 详细解析五大设计原则,帮助开发者深入理解其应用场景和实现方法。


1. 单一职责原则(SRP)

原则:一个类只负责一个功能,避免“上帝类”。

代码示例

违反 SRP 的类
// FileManager 类同时负责文件操作和日志记录,职责不单一
class FileManager {
public:void readFile(const std::string& path) { /* 读取文件 */ }void writeFile(const std::string& path) { /* 写入文件 */ }void logActivity(const std::string& message) { /* 记录日志 */ } // 职责混杂
};
遵循 SRP 的改进
// 职责拆分:FileHandler 只处理文件操作
class FileHandler {
public:void readFile(const std::string& path) { /* 读取文件 */ }void writeFile(const std::string& path) { /* 写入文件 */ }
};// Logger 类专门处理日志记录
class Logger {
public:void log(const std::string& message) { /* 记录日志 */ }
};

关键点:通过拆分职责,代码更易维护和扩展。


2. 开放封闭原则(OCP)

原则:对扩展开放,对修改关闭。

代码示例

违反 OCP 的代码
// 每次新增图形类型都要修改 AreaCalculator 类
class AreaCalculator {
public:double calculateArea(const std::string& type, double param) {if (type == "circle") {return param * param * 3.14;} else if (type == "square") {return param * param;}// 新增类型需修改此处代码}
};
遵循 OCP 的改进
// 抽象基类 Shape
class Shape {
public:virtual double calculateArea() const = 0;virtual ~Shape() = default; // 虚析构函数确保正确释放资源
};// 具体图形类继承 Shape
class Circle : public Shape {
private:double radius;
public:Circle(double r) : radius(r) {}double calculateArea() const override { return radius * radius * 3.14; }
};class Square : public Shape {
private:double side;
public:Square(double s) : side(s) {}double calculateArea() const override { return side * side; }
};// AreaCalculator 类无需修改即可支持新图形
class AreaCalculator {
public:double calculateArea(const Shape& shape) {return shape.calculateArea();}
};

关键点:通过继承和多态实现扩展,避免修改已有代码。


3. 里氏替换原则(LSP)

原则:子类必须能替换基类,且不破坏程序逻辑。

代码示例

违反 LSP 的代码
class Rectangle {
protected:int width, height;
public:virtual void setWidth(int w) { width = w; }virtual void setHeight(int h) { height = h; }int getArea() const { return width * height; }
};// Square 继承 Rectangle 导致逻辑矛盾
class Square : public Rectangle {
public:void setWidth(int w) override { width = w;height = w; // 强制宽高相等}void setHeight(int h) override { width = h;height = h;}
};// 使用基类指针时出现错误行为
Rectangle* rect = new Square();
rect->setWidth(5);
rect->setHeight(4);
std::cout << rect->getArea(); // 输出 16,而非预期的 20
遵循 LSP 的改进
// 抽象基类 Shape
class Shape {
public:virtual int getArea() const = 0;virtual ~Shape() = default;
};// 独立实现 Rectangle 和 Square
class Rectangle : public Shape {
private:int width, height;
public:void setWidth(int w) { width = w; }void setHeight(int h) { height = h; }int getArea() const override { return width * height; }
};class Square : public Shape {
private:int side;
public:void setSide(int s) { side = s; }int getArea() const override { return side * side; }
};

关键点:避免通过继承破坏基类行为,子类应独立实现抽象接口。


4. 接口隔离原则(ISP)

原则:客户端不应依赖它不需要的接口。

代码示例

违反 ISP 的接口
// 臃肿的 IWorker 接口
class IWorker {
public:virtual void work() = 0;virtual void eat() = 0;  // 不需要的方法virtual void sleep() = 0;
};// Programmer 被迫实现无关方法
class Programmer : public IWorker {
public:void work() override { /* 写代码 */ }void eat() override { /* 冗余实现 */ }void sleep() override { /* 冗余实现 */ }
};
遵循 ISP 的改进
// 拆分接口为更小粒度的抽象
class IWorkable {
public:virtual void work() = 0;virtual ~IWorkable() = default;
};class IEatable {
public:virtual void eat() = 0;virtual ~IEatable() = default;
};// Programmer 按需实现接口
class Programmer : public IWorkable, public IEatable {
public:void work() override { /* 写代码 */ }void eat() override { /* 吃饭 */ }
};

关键点:通过接口拆分减少冗余依赖。


5. 依赖倒置原则(DIP)

原则:高层模块依赖抽象,而非具体实现。

代码示例

违反 DIP 的代码
// 高层模块 UserService 直接依赖 MySQLDatabase
class MySQLDatabase {
public:void saveData(const std::string& data) { /* 保存到 MySQL */ }
};class UserService {
private:MySQLDatabase database; // 强耦合具体实现
public:UserService() {}
};
遵循 DIP 的改进
// 定义抽象接口 IDatabase
class IDatabase {
public:virtual void saveData(const std::string& data) = 0;virtual ~IDatabase() = default;
};// 具体实现类
class MySQLDatabase : public IDatabase {
public:void saveData(const std::string& data) override { /* 保存到 MySQL */ }
};class OracleDatabase : public IDatabase {
public:void saveData(const std::string& data) override { /* 保存到 Oracle */ }
};// 高层模块通过抽象接口依赖
class UserService {
private:IDatabase* database; // 依赖抽象
public:UserService(IDatabase* db) : database(db) {} // 依赖注入void saveUserData(const std::string& data) {database->saveData(data);}
};

关键点:通过依赖注入和抽象接口降低耦合。


其他重要原则

1. 合成复用原则(CARP)

原则:优先使用组合而非继承。

代码示例
// 继承方式(不灵活)
class Bird {
public:virtual void fly() { /* 飞行 */ }
};class Penguin : public Bird {
public:void fly() override { throw std::logic_error("企鹅不会飞!"); }
};// 组合方式(灵活扩展)
class FlyBehavior {
public:virtual void fly() = 0;virtual ~FlyBehavior() = default;
};class Bird {
private:FlyBehavior* flyBehavior;
public:Bird(FlyBehavior* fb) : flyBehavior(fb) {}void performFly() { flyBehavior->fly(); }
};class NoFly : public FlyBehavior {
public:void fly() override { /* 空实现 */ }
};// 使用组合构建不会飞的企鹅
Bird penguin(new NoFly());

2. 迪米特法则(LoD)

原则:减少对象间的直接依赖。

代码示例
// 违反 LoD
class Customer {
public:class Wallet {public:double money;};Wallet wallet;
};// 直接访问 Wallet 内部成员(不安全)
Customer customer;
double money = customer.wallet.money;// 遵循 LoD
class Customer {
private:class Wallet {private:double money;public:double getMoney() const { return money; }};Wallet wallet;
public:double getMoney() const { return wallet.getMoney(); }
};

总结

  • SOLID 原则 是面向对象设计的核心,帮助构建高内聚、低耦合的代码。
  • C++ 实现关键点
    • 使用虚函数和纯虚接口实现多态。
    • 通过组合和依赖注入替代继承。
    • 注意内存管理(如智能指针 std::unique_ptr)。
  • 实际开发建议:根据需求灵活权衡设计,避免过度工程化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9962.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++:多继承习题5

题目内容&#xff1a; 先建立一个Point(点)类&#xff0c;包含数据成员x,y(坐标点)。以它为基类&#xff0c;派生出一个Circle(圆)类&#xff0c;增加数据成员r(半径)&#xff0c;再以Circle类为直接基类&#xff0c;派生出一个Cylinder(圆柱体)类&#xff0c;再增加数据成员h…

基于阿里云百炼大模型Sensevoice-1的语音识别与文本保存工具开发

基于阿里云百炼大模型Sensevoice-1的语音识别与文本保存工具开发 摘要 随着人工智能技术的不断发展&#xff0c;语音识别在会议记录、语音笔记等场景中得到了广泛应用。本文介绍了一个基于Python和阿里云百炼大模型的语音识别与文本保存工具的开发过程。该工具能够高效地识别东…

buu-pwn1_sctf_2016-好久不见29

这个也是栈溢出&#xff0c;不一样的点是&#xff0c;有replace替换&#xff0c;要输入0x3c字符&#xff08;60&#xff09;&#xff0c;Iyou 所以&#xff0c;20个I就行&#xff0c;找后面函数 输出提示信息&#xff0c;要求用户输入关于自己的信息。 使用fgets函数从标准输入…

【C语言】在Windows上为可执行文件.exe添加自定义图标

本文详细介绍了在 Windows 环境下,如何为使用 GCC 编译器编译的 C程序 添加自定义图标,从而生成带有图标的 .exe 可执行文件。通过本文的指导,读者可以了解到所需的条件以及具体的操作步骤,使生成的程序更具专业性和个性化。 目录 1. 准备条件2. 具体步骤步骤 1: 准备资源文…

分布式系统架构怎么搭建?

分布式系统架构 互联网企业的业务飞速发展&#xff0c;促使系统架构不断变化。总体来说&#xff0c;系统架构大致经历了单体应用架构—垂直应用架构—分布式架构—SOA架构—微服务架构的演变&#xff0c;很多互联网企业的系统架构已经向服务化网格&#xff08;Service Mesh&am…

阿里巴巴Qwen团队发布AI模型,可操控PC和手机

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

从 UTC 日期时间字符串获取 Unix 时间戳:C 和 C++ 中的挑战与解决方案

在编程世界里&#xff0c;从 UTC 日期时间字符串获取 Unix 时间戳&#xff0c;看似简单&#xff0c;实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间&#xff0c;然后轻松得到 1737094027&#xff08;从 1970 年 1 月 1 日 00:00:00 UTC 开始经…

ESP32-CAM实验集(WebServer)

WebServer 效果图 已连接 web端 platformio.ini ; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library stor…

DRF开发避坑指南01

在当今快速发展的Web开发领域&#xff0c;Django REST Framework&#xff08;DRF&#xff09;以其强大的功能和灵活性成为了众多开发者的首选。然而&#xff0c;错误的使用方法不仅会导致项目进度延误&#xff0c;还可能影响性能和安全性。本文将从我个人本身遇到的相关坑来给大…

qt-C++笔记之QLine、QRect、QPainterPath、和自定义QGraphicsPathItem、QGraphicsRectItem的区别

qt-C笔记之QLine、QRect、QPainterPath、和自定义QGraphicsPathItem、QGraphicsRectItem的区别 code review! 参考笔记 1.qt-C笔记之重写QGraphicsItem的paint方法(自定义QGraphicsItem) 文章目录 qt-C笔记之QLine、QRect、QPainterPath、和自定义QGraphicsPathItem、QGraphic…

C动态库的生成与在Python和QT中的调用方法

目录 一、动态库生成 1&#xff09;C语言生成动态库 2&#xff09;c类生成动态库 二、动态库调用 1&#xff09;Python调用DLL 2&#xff09;QT调用DLL 三、存在的一些问题 1&#xff09;python调用封装了类的DLL可能调用不成功 2&#xff09;DLL格式不匹配的问题 四、…

.NET MAUI进行UDP通信(二)

上篇文章有写过一个简单的demo&#xff0c;本次对项目进行进一步的扩展&#xff0c;添加tabbar功能。 1.修改AppShell.xaml文件&#xff0c;如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…

什么是Maxscript?为什么要学习Maxscript?

MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…

适配器模式

目录 一、概念 1、定义 2、涉及到的角色 二、类适配器 1、类图 2、代码示例 &#xff08;1&#xff09;水饺&#xff08;源角色&#xff09; &#xff08;2&#xff09;烹饪&#xff08;目的角色&#xff09; &#xff08;3&#xff09;食品适配器&#xff08;适配器角…

YOLO11/ultralytics:环境搭建

前言 人工智能物体识别行业应该已经饱和了吧&#xff1f;或许现在并不是一个好的入行时候。 最近看到了各种各样相关的扩展应用&#xff0c;为了理解它&#xff0c;我不得不去尝试了解一下。 我选择了git里非常受欢迎的yolo系列&#xff0c;并尝试了最新版本YOLO11或者叫它ultr…

SQL注入漏洞之绕过[前端 服务端 waf]限制 以及 防御手法 一篇文章给你搞定

目录 绕过手法 前端代码绕过 后端代码绕过 各种字段进行验证 union 大小写绕过 双写逃过 强制类型判断 引号特殊编码处理。 内联注释绕过 注释符绕过 or/and绕过 空格绕过 防御SQL注入的方法 使用预编译语句 使用存储过程 检查数据类型 绕过手法 前端代码绕过…

使用冒泡排序模拟实现qsort函数

1.冒泡排序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>int main() {int arr[] { 0,2,5,3,4,8,9,7,6,1 };int sz sizeof(arr) / sizeof(arr[0]);//冒泡排序一共排序 sz-1 趟for (int i 0; i < sz - 1; i){//标志位&#xff0c;如果有序&#xff0c;直接…

【Linux】线程互斥与同步

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

【数据结构】二叉树

二叉树 1. 树型结构&#xff08;了解&#xff09;1.1 概念1.2 概念&#xff08;重要&#xff09;1.3 树的表示形式&#xff08;了解&#xff09;1.4 树的应用 2. 二叉树&#xff08;重点&#xff09;2.1 概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的…

1.五子棋对弈python解法——2024年省赛蓝桥杯真题

问题描述 原题传送门&#xff1a;1.五子棋对弈 - 蓝桥云课 "在五子棋的对弈中&#xff0c;友谊的小船说翻就翻&#xff1f;" 不&#xff01;对小蓝和小桥来说&#xff0c;五子棋不仅是棋盘上的较量&#xff0c;更是心与心之间的沟通。这两位挚友秉承着"友谊第…