固原市网站建设_网站建设公司_展示型网站_seo优化
2025/12/18 1:08:18 网站建设 项目流程

前言:在平时coding过程中,大部分程序员可能把更多精力和时间花在功能的实现和完成上面,对于代码的可读性、可读性及可扩展性没有过多的关注,这可能会造成后期功能扩展困难、新人无法理解等问题。这里介绍一些软件代码设计原则,帮助大家提升代码质量。

目录

一、SOLID软件设计原则

二、单一职责原则(Single Responsible Principle)

二、开闭原则(Open Close Principle)

三、里氏替换原则(Liskov Substitution Principle)

四、接口隔离原则(Interface Segregation Principle)

五、依赖倒置原则(Dependency Inversion Principle)


一、SOLID软件设计原则

SOLID是面向对象软件设计中5个基础设计原则的简写,由Robert C. Martin提出,是设计模式的指导思想。这些原则包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。下面章节分别展开介绍。

二、单一职责原则(Single Responsible Principle)

1)定义:一个类应当只做一件事情,只有一个引起它变化的原因

2)思想:避免一个类同时负责多个功能,否则对一个功能的修改可能会影响到其他功能,修改扩展成本增加。

3)示例

反例:一个UserManager类同时负责用户信息管理用户日志记录,如果日志的存储方式修改(比如从本地文件改成数据库),就需要修改这个类,违反了单一职责。

正例:拆分为两个类:

// 只负责用户信息的管理 class UserManager { public: void addUser(const std::string& username) { // 处理用户添加的逻辑 } void deleteUser(const std::string& username) { // 处理用户删除的逻辑 } }; // 只负责用户相关的日志记录 class UserLogger { public: void logUserOperation(const std::string& username, const std::string& operation) { // 处理日志记录的逻辑 } };

二、开闭原则(Open Close Principle)

1)定义:软件模块(类、函数等)应该对扩展开放,对修改关闭。

2)思想:当需要新增功能时,应当通过扩展已有代码模块来实现,而不是在已有代码上直接修改、打补丁,防止补丁上打补丁,时间长了就难以维护了。

3)示例

反例:一个ShapeCalculator类,计算不同图形的面积,如果新增一种图形(比如椭圆),就需要修改类的代码:

// 反例:新增图形需要修改这个类 class ShapeCalculator { public: double calculateArea(const std::string& shapeType, double param1, double param2) { if (shapeType == "circle") { return M_PI * param1 * param1; } else if (shapeType == "rectangle") { return param1 * param2; } // 新增椭圆需要在这里加else if return 0; } };

正例:通过抽象类+继承实现

// 抽象的图形类 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 M_PI * radius * radius; } }; // 矩形类,继承Shape class Rectangle : public Shape { private: double width; double height; }; // 面积计算器,只依赖抽象类,新增图形不需要修改这个类 class ShapeCalculator { public: double calculateArea(const Shape& shape) { return shape.calculateArea(); } };

上述代码中出现了虚函数和抽象类的概念,如需要可参考之前的文章:C++ 虚函数 解析指南-CSDN博客

三、里氏替换原则(Liskov Substitution Principle)

1)定义:子类对象可以替换父类对象在程序中的所有使用场景,且不会改变程序的正确性。

2)思想:子类必须完全实现父类功能,不能破坏父类的行为,避免继承关系的滥用。

3)示例

反例:正方形继承矩形,但是正方形的宽和高必须相等,修改宽的时候高也会变化,破坏了矩形的行为

class Rectangle { protected: double width; double height; public: void setWidth(double w) { width = w; } void setHeight(double h) { height = h; } double getArea() { return width * height; } }; // 反例:正方形继承矩形,但是setWidth和setHeight的行为不符合父类的约定 class Square : public Rectangle { public: void setWidth(double w) override { width = w; height = w; } void setHeight(double h) override { width = h; height = h; } };

正例:重新设计继承关系,使得正方形与矩形都继承抽象的Shape类。

四、接口隔离原则(Interface Segregation Principle)

1)定义:客户端不应当依赖它不需要的接口,一个类对另一个类的依赖应当建立在最小接口上。

2)思想:避免设计大而全的接口,接口应当拆分多个小的,高内聚、低耦合。

3)示例

反例:一个大的Worker接口,包含了所有工作相关的方法,但是不同的 worker 只需要其中一部分

// 反例:大而全的接口 class Worker { public: virtual void work() = 0; virtual void eat() = 0; virtual void sleep() = 0; }; // 机器人只需要work,但是必须实现eat和sleep class Robot : public Worker { public: void work() override { /* 工作逻辑 */ } void eat() override { /* 机器人不需要吃饭,空实现 */ } void sleep() override { /* 机器人不需要睡觉,空实现 */ } };

正例:拆分为小接口

// 只包含工作的接口 class Workable { public: virtual void work() = 0; virtual ~Workable() = default; }; // 只包含休息相关的接口 class Restable { public: virtual void eat() = 0; virtual void sleep() = 0; virtual ~Restable() = default; }; // 机器人只实现Workable接口 class Robot : public Workable { public: void work() override { /* 工作逻辑 */ } }; // 人类实现Workable和Restable接口 class HumanWorker : public Workable, public Restable { public: void work() override { /* 工作逻辑 */ } void eat() override { /* 吃饭逻辑 */ } void sleep() override { /* 睡觉逻辑 */ } };

五、依赖倒置原则(Dependency Inversion Principle)

1)定义:上层模块不应当依赖底层模块抽象不应当依赖细节,细节应当依赖抽象。

2)思想:通过抽象(抽象类或接口)实现上层与底层解耦,使得模块之间依赖关系倒置,提高灵活性。

3)示例

反例:上层模块ReportGenerator直接依赖底层模块MySQLDatabase,如果需要切换数据库(比如改成PostgreSQL),就需要修改ReportGenerator

// 反例:上层依赖底层的具体实现 class MySQLDatabase { public: std::string getData() { return "从MySQL获取的数据"; } }; class ReportGenerator { private: MySQLDatabase db; public: std::string generateReport() { return "报告内容:" + db.getData(); } };

正例:通过抽象接口解耦

// 抽象的数据库接口 class Database { public: virtual std::string getData() const = 0; virtual ~Database() = default; }; // MySQL的具体实现,依赖抽象接口 class MySQLDatabase : public Database { public: std::string getData() const override { return "从MySQL获取的数据"; } }; // PostgreSQL的具体实现,依赖抽象接口 class PostgreSQLDatabase : public Database { public: std::string getData() const override { return "从PostgreSQL获取的数据"; } }; // 高层模块依赖抽象接口,而不是具体实现 class ReportGenerator { private: const Database& db; public: // 通过构造函数注入依赖 ReportGenerator(const Database& database) : db(database) {} std::string generateReport() { return "报告内容:" + db.getData(); } };

结束语:一般这5种原则不会孤立存在,通常两三个会结合在一起使用。读者可参考进行理解消化,最终目的是应用于实际项目中,实现代码质量显著提升。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询