深圳一百讯网站建设做电商网站搭建就业岗位
深圳一百讯网站建设,做电商网站搭建就业岗位,天津平台网站建设公司,深圳网站制作 优选灵点网络设计模式——七大设计原则 1、单一职责原则#xff08;SRP#xff09;2、开放封闭原则#xff08;OCP#xff09;3、依赖倒转原则#xff08;DIP#xff09;4、里氏替换原则 (LSP)5、接口隔离原则 (ISP)6、合成/聚合复用原则 (CARP)7、迪米特法则 (LoD) 了解 设计模式 的… 设计模式——七大设计原则 1、单一职责原则SRP2、开放封闭原则OCP3、依赖倒转原则DIP4、里氏替换原则 (LSP)5、接口隔离原则 (ISP)6、合成/聚合复用原则 (CARP)7、迪米特法则 (LoD) 了解 设计模式 的朋友们想必都听说过“七大设计原则”吧。我们在进行程序设计的时候要尽可能地保证程序的 可扩展性、可维护性 和 可读性最经典的 23 种设计模式中或多或少地都在使用这些设计原则也就是说设计模式是站在设计原则的基础之上的。所以在学习设计模式之前很有必要对这些设计原则先做一下了解。 1、单一职责原则SRP There should never be more than one reason for a class to change. 理解一个类只负责一项职责不同的类具备不同的职责各司其职。 面向对象三大特性之一的 封装 指的就是将单一事物抽象出来组合成一个类所以我们在设计类的时候每个类中处理的是单一事物而不是某些事物的集合。 设计模式中所谓的 单一职责原则(Single Responsibility Principle - SRP)就是对一个类而言应该仅有一个引起它变化的原因其实就是将这个类所承担的职责单一化。如果一个类承担的职责过多就等于把这些 职责耦合 到了一起一个职责的变化可能会 削弱或者抑制 这个类完成其他职责的能力。这种耦合会导致设计变得脆弱当变化发生时设计会遭受到意想不到的破坏。
#include iostream
#include string// 单一职责原则示例一个类只负责一个职责class File {
public:void writeToFile(const std::string data) {// 写入文件的具体实现std::cout Writing to file: data std::endl;}
};class Logger {
public:void log(const std::string message) {// 记录日志的具体实现std::cout Logging: message std::endl;}
};int main() {File file;file.writeToFile(Data to be written);Logger logger;logger.log(Log message);return 0;
}软件设计真正要做的事情就是发现根据需求发现职责并把这些职责进行分离添加新的类给当前类减负越是这样项目才越容易维护。杜绝万能类或万能函数
2、开放封闭原则OCP Software entities like classes,modules and functions should be open for extension but closed for modifications. 理解类、模块、函数对 扩展开放对 修改封闭。 开放 – 封闭原则 (Open/Closed Principle - OCP) 说的是软件实体类、模块、函数等可以扩展但是不可以修改。也就是说对于扩展是开放的对于修改是封闭的。 该原则是程序设计的一种理想模式在很多情况下无法做到完全的封闭。但是作为设计人员应该能够对自己设计的模块在哪些位置产生何种变化了然于胸因此 需要在这些位置创建 抽象类 来隔离以后发生的这些同类变化其实就是对 多态 的应用创建新的子类并重写父类虚函数用以更新处理动作。 此处的 抽象类其实并不等价与C中完全意义上是 抽象类 需要有纯虚函数这里所说的 抽象类 只需要包含虚函数纯虚函数 或 非纯虚函数能够实现 多态 即可。 #include iostream
#include vector// 开闭原则示例通过抽象类和继承来实现开闭原则class Shape {
public:virtual void draw() const 0;
};class Circle : public Shape {
public:void draw() const override {std::cout Drawing Circle std::endl;}
};class Square : public Shape {
public:void draw() const override {std::cout Drawing Square std::endl;}
};class Drawing {
public:void drawShapes(const std::vectorShape* shapes) const {for (const auto shape : shapes) {shape-draw();}}
};int main() {Circle circle;Square square;Drawing drawing;std::vectorShape* shapes {circle, square};drawing.drawShapes(shapes);return 0;
}开放 – 封闭原则 是面向对象设计的核心所在这样可以给我们设计出的程序带来巨大的好处使其可维护性、可扩展性、可复用性、灵活性更好。
3、依赖倒转原则DIP High level modules should not depends upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions. 理解高层模块 不应该依赖于底层模块具体而 应该依赖于抽象。面向接口编程 关于依赖倒转原则对应的是两条非常抽象的描述
高层模块不应该依赖低层模块两个都应该依赖抽象。抽象不应该依赖细节细节应该依赖抽象。 先用人话解释一下这两句话中的一些抽象概念
高层模块可以理解为上层应用就是业务层的实现低层模块可以理解为底层接口比如封装好的API、动态库等抽象指的就是抽象类或者接口在C中没有接口只有抽象类。
先举一个 高层模块 依赖 低层模块的例子: 大聪明的项目组接了一个新项目低层使用的是 MySql 的数据库接口高层基于这套接口对数据库表进行了添删查改实现了对业务层数据的处理。而后由于某些原因数据超大规模和高并发的需求所以更换了 Redis 数据库由于低层的数据库接口变了高层代码的数据库操作部分是直接调用了低层的接口因此也需要进行对应的修改无法实现对高层代码的直接复用大聪明欲哭无泪。 通过上面的例子可以得知当依赖的低层模块变了就会牵一发而动全身如果这样设计项目架构对于程序猿来说其工作量无疑是很重的。 // 依赖倒置原则示例高层模块不应该依赖于底层模块二者都应该依赖于抽象// 数据库接口抽象类
class Database {
public:virtual void connect() 0;virtual void query(const std::string sql) 0;virtual void disconnect() 0;
};// MySQL 数据库实现低层模块
class MySQLDatabase : public Database {
public:void connect() override {// 连接 MySQL 数据库的具体实现}void query(const std::string sql) override {// 执行 MySQL 查询的具体实现}void disconnect() override {// 断开 MySQL 数据库连接的具体实现}
};// Redis 数据库实现低层模块
class RedisDatabase : public Database {
public:void connect() override {// 连接 Redis 数据库的具体实现}void query(const std::string command) override {// 执行 Redis 命令的具体实现}void disconnect() override {// 断开 Redis 数据库连接的具体实现}
};//高层模块
class AppService {
private:Database* database;public:AppService(Database* db) : database(db) {}void performTask() {database-connect();database-query(SELECT * FROM data);// 执行其他操作database-disconnect();}
};如果要搞明白这个案例的解决方案以及 抽象和细节 之间的依赖关系需要先了解另一个原则 — 里氏替换原则。
4、里氏替换原则 (LSP) Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. 理解父类可被子类替换但 反之不一定成立。 所谓的里氏替换原则就是子类类型必须能够替换掉它们的父类类型。 关于这个原理的应用其实也很常见比如在Qt中所有窗口类型的类的构造函数都有一个 QWidget* 类型的参数QWidget类 是所有窗口的 基类通过这个参数指定当前窗口的父对象。虽然参数是窗口类的基类类型但是我们在给其指定实参的大多数时候指定的都是 子类的对象其实也就是相当于使用子类类型 替换掉了 它们的 父类类型。 这个原则的要满足的第一个条件就是 继承其次还要求子类继承的所有父类的属性和方法对于子类来说都是合理的。关于这个是否合理下面举个栗子 比如对于哺乳动物来说都是胎生但是有一种特殊的存在就是鸭嘴兽它虽然是哺乳动物但是是卵生。 如果我们设计了两个类哺乳动物类 和 鸭嘴兽类此时能够让鸭嘴兽类继承哺乳动物类吗 答案肯定是否定的因为如果我们这么做了鸭嘴兽就继承了胎生属性这个属性和它自身的情况是不匹配的。如果想要遵循里氏替换原则我们就不能让着两个类有继承关系。 如果我们创建了其它的胎生的哺乳动物类那么它们是可以继承哺乳动物这个类的在实际应用中就可以使用子类替换掉父类同时功能也不会受到影响父类实现了复用子类也能在父类的基础上增加新的行为这个就是 里氏替换原则。 #include iostream// 里氏替换原则示例派生类可以替代基类// 基类哺乳动物
class Mammal {
protected:bool isViviparous; // 出生方式为胎生public:virtual void giveBirth() const {std::cout Giving birth std::endl;}
};// 派生类狗
class Dog : public Mammal {
public:// 重写基类的 giveBirth 方法void giveBirth() const override {std::cout Dog giving birth to puppies std::endl;}
};// 派生类猫
class Cat : public Mammal {
public:// 重写基类的 giveBirth 方法void giveBirth() const override {std::cout Cat giving birth to kittens std::endl;}
};// 函数繁殖哺乳动物
void reproduce(const Mammal animal) {animal.giveBirth();
}int main() {Dog myDog;Cat myCat;// 使用 Dog 对象std::cout Dog: ;reproduce(myDog); // 输出: Dog giving birth to puppies// 使用 Cat 对象std::cout Cat: ;reproduce(myCat); // 输出: Cat giving birth to kittensreturn 0;
}上面在讲 依赖倒转原则 的时候说过抽象不应该依赖细节细节应该依赖抽象。也就意味着我们应该对细节进行封装在C中就是将其放到一个抽象类中C中没有接口不能像Java一样封装成接口每个细节就相当于上面例子中的哺乳动物的一个特性这样一来这个抽象的哺乳动物类就成了项目架构中高层和低层的桥梁将二者整合到一起。
抽象类中提供的接口是固定不变的低层模块是抽象类的子类继承了抽象类的接口并且可以重写这些接口的行为高层模块想要实现某些功能调用的是抽象类中的函数接口并且是通过抽象类的父类指针引用其子类的实例对象用子类类型替换父类类型这样就实现了多态。 基于 依赖倒转原则 将项目的结构换成上图的这种模式之后低层模块发生变化对应高层模块是没有任何影响的这样程序猿的工作量降低了代码也更容易维护说白了依赖倒转原则就是对多态的典型应用。
5、接口隔离原则 (ISP) The dependency of one class to another one should depend on the smallest possible interface. 理解使用多个专门的接口而不要使用一个单一的大接口接口单一职责 这个原则的意思是使用多个隔离的接口比使用单个接口要好。它还有另外一个意思是降低类之间的耦合度。由此可见其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想它强调降低依赖降低耦合。 在C中没有接口只有抽象类 #include iostream// 接口隔离原则示例客户端不应该被迫依赖它不使用的接口class Worker {
public:virtual void work() const 0;
};class Eater {
public:virtual void eat() const 0;
};class Robot : public Worker {
public:void work() const override {std::cout Robot is working std::endl;}
};class Human : public Worker, public Eater {
public:void work() const override {std::cout Human is working std::endl;}void eat() const override {std::cout Human is eating std::endl;}
};int main() {Robot robot;Human human;robot.work(); // 输出: Robot is workinghuman.work(); // 输出: Human is workinghuman.eat(); // 输出: Human is eatingreturn 0;
}6、合成/聚合复用原则 (CARP) Strive for a design where you compose classes from smaller, more independent units, rather than inheriting from a single, monolithic base class. 理解尽量 使用组合/聚合而 不是继承。 #include iostream// 合成/聚合复用原则示例优先使用合成/聚合而不是继承class Engine {
public:void start() const {std::cout Engine started std::endl;}
};class Car {
private:Engine engine;public:void start() const {engine.start();std::cout Car started std::endl;}
};int main() {Car car;car.start(); // 输出: Engine started, Car startedreturn 0;
}7、迪米特法则 (LoD) Only talk to you immediate friends. 理解尽量 减少对象之间的交互从而减小类之间的耦合。 迪米特法则 ( Law of Demeter - LoD ) 又叫 最少知道原则 即一个类对自己依赖的类知道的越少越好。也就是说对于被依赖的类不管多么复杂都尽量将逻辑封装在类的内部。对外除了提供的 public 方法不对外泄露任何信息。
迪米特法则还有个更简单的定义只与直接的朋友通信。
直接的朋友 每个对象都会与其他对象有耦合关系只要两个对象之间有耦合关系 我们就说这两个对象之间是朋友关系。耦合的方式很多依赖关联组合聚合 等。其中我们称出现成员变量方法参数方法返回值中的类为直接的朋友而 出现在局部变量中的类不是直接的朋友。也就是说陌生的类最好不要以局部变量 的形式出现在类的内部。
#include iostream// 迪米特法则示例一个对象应该对其它对象有尽可能少的了解class Teacher {
public:void teach() const {std::cout Teaching... std::endl;}
};class Student {
public:void learn() const {std::cout Learning... std::endl;}
};class School {
private:Teacher teacher;Student student;public:void conductClass() const {teacher.teach();student.learn();std::cout Class is conducted std::endl;}
};int main() {School school;school.conductClass(); // 输出: Teaching..., Learning..., Class is conductedreturn 0;
}一定要做到低耦合、高内聚。
注仅供学习参考如有不足欢迎指正
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/85274.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!