东莞+网站+建设+汽车,个人做视频网站,淘宝客网站模板免费下载,discuz 做企业网站1、什么是SOLID设计原则
SOLID 是面向对象设计中的五个基本设计原则的首字母缩写#xff0c;它们是#xff1a;
单一职责原则#xff08;Single Responsibility Principle#xff0c;SRP#xff09;#xff1a; 类应该只有一个单一的职责#xff0c;即一个类应该有且只…1、什么是SOLID设计原则
SOLID 是面向对象设计中的五个基本设计原则的首字母缩写它们是
单一职责原则Single Responsibility PrincipleSRP 类应该只有一个单一的职责即一个类应该有且只有一个改变的理由。这意味着一个类应该只负责一个特定的功能或任务而不是多个不相关的功能。这样做可以提高类的内聚性并使得类更容易理解、修改和测试。
开放-封闭原则Open/Closed PrincipleOCP 软件实体类、模块、函数等应该对扩展开放对修改封闭。这意味着在不修改现有代码的情况下应该能够通过添加新的代码来扩展系统的功能。这样做可以使得系统更加稳定减少修改现有代码可能带来的风险。
里氏替换原则Liskov Substitution PrincipleLSP 子类型必须能够替换其基类型。换句话说任何可以接受基类型的地方都可以接受子类型而且不会引发意外的行为。这样做可以保持系统的一致性和可靠性并且确保使用继承时不会破坏代码的正确性。
接口隔离原则Interface Segregation PrincipleISP 客户端不应该被迫依赖于其不使用的接口。这意味着应该将接口设计成小而专注的接口而不是大而臃肿的接口。这样做可以降低耦合性并且使得系统更加灵活和易于维护。
依赖倒置原则Dependency Inversion PrincipleDIP 高层模块不应该依赖于低层模块二者都应该依赖于抽象。抽象不应该依赖于具体实现具体实现应该依赖于抽象。这样做可以降低模块之间的耦合度并且使得系统更易于扩展和修改。
这些原则是由罗伯特·马丁Robert C. Martin等人在面向对象设计中提出的它们提供了一套指导原则帮助设计出高质量、可维护和可扩展的面向对象系统。
2、单一职责原则
单一职责原则Single Responsibility PrincipleSRP要求一个类或模块应该只有一个单一的责任即一个类或模块应该只负责一个特定的功能或任务。这样做可以提高代码的内聚性、可维护性和可测试性。
让我们通过一个简单的例子来说明单一职责原则
假设我们有一个简单的应用程序用于处理用户信息包括保存用户信息到数据库和从数据库中检索用户信息。我们可以将这个功能拆分成两个类一个负责保存用户信息一个负责检索用户信息。
#include iostream
#include string// 负责保存用户信息到数据库
class UserSaver {
public:void saveUser(const std::string username, const std::string email) {// 将用户信息保存到数据库std::cout 用户信息已保存到数据库 username , email std::endl;}
};// 负责从数据库中检索用户信息
class UserRetriever {
public:void retrieveUser(const std::string username) {// 从数据库中检索用户信息std::cout 从数据库中检索到用户信息 username std::endl;}
};int main() {UserSaver userSaver;userSaver.saveUser(Alice, aliceexample.com);UserRetriever userRetriever;userRetriever.retrieveUser(Alice);return 0;
}在这个例子中我们有两个类 UserSaver 和 UserRetriever它们分别负责保存用户信息和检索用户信息。这两个类各自都只有一个单一的职责即负责一个特定的功能。如果我们需要修改保存用户信息的逻辑我们只需要修改 UserSaver 类如果我们需要修改检索用户信息的逻辑我们只需要修改 UserRetriever 类。这样做提高了代码的可维护性并且使得每个类更加简单和易于理解。
3、开放-封闭原则
开放-封闭原则Open/Closed PrincipleOCP是面向对象设计中的一个基本原则由柏拉图·梅特克斯Bertrand Meyer在他的《面向对象软件构造》Object-Oriented Software Construction一书中首次提出。它的核心思想是软件实体类、模块、函数等应该对扩展开放对修改封闭。换句话说软件实体在不修改现有代码的情况下应该能够通过添加新的代码来扩展系统的功能。
开放-封闭原则的目的是为了提高系统的可维护性、可扩展性和稳定性。通过遵循这一原则可以使得系统更容易理解和修改并且减少对现有代码的影响。
在实际应用中可以通过以下几种方式来遵循开放-封闭原则
抽象化通过使用抽象类、接口或者抽象函数来定义可扩展的接口从而使得系统可以根据需要进行扩展而不必修改现有代码。
多态性利用多态性和继承机制使得系统可以通过添加新的子类来扩展功能而不必修改基类或现有代码。
组合/聚合通过组合或聚合关系来构建对象之间的关联关系从而使得系统可以通过添加新的组件来扩展功能而不必修改现有组件。
模块化将系统分解成独立的模块或组件使得每个模块只负责一个特定的功能从而使得系统可以通过添加新的模块来扩展功能而不必修改现有模块。
总之开放-封闭原则指导我们设计出易于扩展和维护的软件系统通过封装变化和利用多态性使得系统可以根据需要进行扩展而不必修改现有代码。
让我们通过一个简单的例子来说明开放-封闭原则。
假设我们有一个简单的图形绘制程序它可以绘制不同形状的图形包括圆形和矩形。现在我们希望在程序中添加新的图形类型比如三角形。我们可以通过遵循开放-封闭原则来扩展程序的功能而不必修改现有的代码。
首先我们定义一个抽象基类 Shape它有一个纯虚函数 draw 用于绘制图形
#include iostream// 抽象基类图形
class Shape {
public:virtual void draw() const 0;
};然后我们定义具体的图形类比如 Circle 和 Rectangle 类它们分别继承自 Shape 类并实现 draw 函数
// 圆形类
class Circle : public Shape {
public:void draw() const override {std::cout 绘制圆形\n;}
};// 矩形类
class Rectangle : public Shape {
public:void draw() const override {std::cout 绘制矩形\n;}
};现在如果我们想要添加新的图形类型比如三角形我们只需要添加一个新的类 Triangle它也继承自 Shape 类并实现 draw 函数
// 三角形类
class Triangle : public Shape {
public:void draw() const override {std::cout 绘制三角形\n;}
};通过这种方式我们可以在不修改现有代码的情况下通过添加新的类来扩展程序的功能符合开放-封闭原则。这样做提高了代码的可维护性和可扩展性使得系统更易于理解和修改。
4、 里氏替换原则
里氏替换原则Liskov Substitution PrincipleLSP是面向对象设计中的一个基本原则由芭芭拉·利斯科夫Barbara Liskov在 1987 年提出。该原则指出子类型必须能够替换其基类型即任何可以接受基类型的地方都可以接受子类型而且不会引发意外的行为。
在更通俗的说法中如果一个类型是子类型派生类那么它应该可以替换掉基类型基类并且不会破坏程序的正确性。换句话说子类型应该保持基类型的行为而不是产生意外的行为。
遵循里氏替换原则的目的是为了确保代码的一致性和可靠性使得系统更易于理解、扩展和维护。如果违反了里氏替换原则那么可能会导致程序的错误行为和不稳定性。
在实际应用中可以通过以下几点来遵循里氏替换原则
子类型必须实现基类型的所有行为不能减少基类型的约束条件。 子类型可以增加新的行为但不能修改基类型已有的行为。 子类型的前置条件即输入条件必须比基类型更宽松。 子类型的后置条件即输出条件必须比基类型更严格。
通过遵循里氏替换原则可以确保系统的稳定性和可靠性并且使得系统更易于扩展和维护。
让我们通过一个简单的例子来说明里氏替换原则。
假设我们有一个简单的几何图形类层次结构包括基类 Shape 和两个子类 Rectangle 和 Square其中 Square 是 Rectangle 的子类。
现在让我们来看看是否满足里氏替换原则
#include iostream// 基类图形
class Shape {
public:virtual void draw() const {std::cout 绘制图形\n;}
};// 矩形类
class Rectangle : public Shape {
public:void draw() const override {std::cout 绘制矩形\n;}
};// 正方形类
class Square : public Rectangle {
public:void draw() const override {std::cout 绘制正方形\n;}
};// 绘制图形函数
void drawShape(const Shape shape) {shape.draw();
}int main() {Rectangle rectangle;Square square;drawShape(rectangle); // 绘制矩形drawShape(square); // 绘制正方形return 0;
}在这个例子中我们有一个基类 Shape它有一个 draw 方法用于绘制图形。然后我们有一个 Rectangle 类和一个 Square 类它们分别继承自 Shape 类并且都重写了 draw 方法以实现各自特定的绘制行为。
在 main 函数中我们创建了一个 Rectangle 对象和一个 Square 对象并且分别调用了 drawShape 函数来绘制这些图形。
在这个例子中Square 类是 Rectangle 类的子类符合继承关系。而且在 drawShape 函数中我们可以接受 Shape 类型的参数并且传入 Rectangle 或 Square 对象进行绘制而不会产生意外的行为。
因此这个例子满足了里氏替换原则子类型Square可以替换其基类型Rectangle而不会引发意外的行为程序的行为保持一致。
5、接口隔离原则
接口隔离原则Interface Segregation PrincipleISP是面向对象设计中的一个基本原则由罗伯特·马丁Robert C. Martin在他的《敏捷软件开发原则、模式和实践》Agile Software Development, Principles, Patterns, and Practices一书中提出。接口隔离原则指出客户端不应该被迫依赖于其不使用的接口。换句话说一个类不应该依赖于它不需要使用的接口应该将接口设计成小而专注的接口而不是大而臃肿的接口。
接口隔离原则的目的是为了提高系统的灵活性和可维护性。通过将接口拆分成小而专注的接口可以降低类之间的耦合度使得系统更易于理解、扩展和修改。同时这也可以避免因为接口的臃肿而导致的功能耦合和代码冗余。
在实践中可以通过以下几点来遵循接口隔离原则
将大而臃肿的接口拆分成多个小而专注的接口每个接口只包含一个单一的功能或职责。只在需要使用某个接口的地方引入该接口避免将不需要的接口强加给客户端。根据客户端的需求设计出合适的接口并且保持接口的稳定性避免频繁地修改接口。
通过遵循接口隔离原则可以使得系统更灵活、更易于维护并且能够更好地应对需求变化。
让我们通过一个简单的例子来说明接口隔离原则。
假设我们有一个简单的文件操作接口 FileOperation它定义了一些文件操作的方法比如打开文件、读取文件和关闭文件等。然后我们有两个类 TextEditor 和 ImageEditor它们分别实现了这个接口。
首先让我们定义文件操作接口 FileOperation
#include iostream// 文件操作接口
class FileOperation {
public:virtual void open() 0;virtual void read() 0;virtual void close() 0;
};然后我们有一个文本编辑器 TextEditor它需要实现文件操作接口来打开和读取文本文件
// 文本编辑器类
class TextEditor : public FileOperation {
public:void open() override {std::cout 打开文本文件\n;}void read() override {std::cout 读取文本文件\n;}void close() override {std::cout 关闭文本文件\n;}
};接着我们有一个图像编辑器 ImageEditor它也需要实现文件操作接口来打开和读取图像文件
// 图像编辑器类
class ImageEditor : public FileOperation {
public:void open() override {std::cout 打开图像文件\n;}void read() override {std::cout 读取图像文件\n;}void close() override {std::cout 关闭图像文件\n;}
};在这个例子中TextEditor 和 ImageEditor 都实现了 FileOperation 接口但是它们只使用了其中的一部分方法即打开和读取文件。如果我们将所有文件操作都放在一个大的接口中那么 TextEditor 和 ImageEditor 就不得不实现它们不需要的方法违反了接口隔离原则。
通过将接口设计成小而专注的接口每个接口只包含一个单一的功能或职责我们遵循了接口隔离原则并且使得系统更易于理解、扩展和修改。
6、依赖倒置原则
依赖倒置原则Dependency Inversion PrincipleDIP是面向对象设计中的一个基本原则由罗伯特·马丁Robert C. Martin在他的《敏捷软件开发原则、模式和实践》Agile Software Development, Principles, Patterns, and Practices一书中提出。依赖倒置原则指出高层模块不应该依赖于低层模块二者都应该依赖于抽象。抽象不应该依赖于具体实现具体实现应该依赖于抽象。
依赖倒置原则的核心思想是通过使用抽象来降低类之间的耦合度从而使得系统更加灵活、可扩展和易于维护。具体来说依赖倒置原则要求我们将程序的设计重心放在抽象上而不是具体实现上通过使用接口、抽象类或者依赖注入等方式来实现依赖倒置。
在实践中可以通过以下几点来遵循依赖倒置原则
高层模块不应该依赖于低层模块二者都应该依赖于抽象。即高层模块和低层模块都应该依赖于同一个抽象接口或抽象类。抽象不应该依赖于具体实现具体实现应该依赖于抽象。即抽象接口或抽象类不应该依赖于具体实现而是具体实现应该依赖于抽象接口或抽象类。可以通过依赖注入Dependency Injection等方式来实现依赖倒置将具体实现的创建和注入交给外部而不是在类内部创建具体实现的对象。
通过遵循依赖倒置原则可以使得系统更加灵活、可扩展和易于维护减少类之间的耦合度提高代码的可复用性和可测试性。
让我们通过一个简单的例子来说明依赖倒置原则。
假设我们有一个简单的电子邮件发送系统其中包含一个 EmailSender 类用于发送电子邮件。一开始EmailSender 类直接依赖于具体的邮件服务提供商比如 Gmail。这个设计违反了依赖倒置原则因为高层模块 EmailSender 直接依赖于低层模块即具体的邮件服务提供商。
#include iostream// 具体的邮件服务提供商Gmail
class Gmail {
public:void sendEmail(const std::string recipient, const std::string message) {std::cout Sending email to recipient via Gmail: message std::endl;}
};// 邮件发送类
class EmailSender {
private:Gmail gmail;public:void sendEmail(const std::string recipient, const std::string message) {gmail.sendEmail(recipient, message);}
};int main() {EmailSender sender;sender.sendEmail(exampleexample.com, Hello, this is a test email.);return 0;
}现在让我们通过引入抽象来遵循依赖倒置原则。我们可以定义一个抽象的邮件服务接口 EmailService并让 Gmail 类实现这个接口。然后EmailSender 类只依赖于 EmailService 接口而不是具体的邮件服务提供商。
#include iostream// 抽象的邮件服务接口
class EmailService {
public:virtual void sendEmail(const std::string recipient, const std::string message) 0;
};// 具体的邮件服务提供商Gmail
class Gmail : public EmailService {
public:void sendEmail(const std::string recipient, const std::string message) override {std::cout Sending email to recipient via Gmail: message std::endl;}
};// 邮件发送类
class EmailSender {
private:EmailService* emailService;public:EmailSender(EmailService* service) : emailService(service) {}void sendEmail(const std::string recipient, const std::string message) {emailService-sendEmail(recipient, message);}
};int main() {Gmail gmail;EmailSender sender(gmail);sender.sendEmail(exampleexample.com, Hello, this is a test email.);return 0;
}通过这种方式EmailSender 类不再直接依赖于具体的邮件服务提供商而是依赖于抽象的邮件服务接口 EmailService。这样做符合依赖倒置原则使得系统更加灵活、可扩展和易于维护因为现在可以轻松地切换不同的邮件服务提供商而不需要修改 EmailSender 类的代码。