软件设计原则 1小时系列 (C++版)

文章目录

  • 前言
    • 基本概念
  • Design Principles
    • ⭐单一职责原则
      • (SRP) Single Responsibility Principle
      • Code
    • ⭐里氏替换原则
      • (LSP) Liskov Substitution Principle
      • Code
    • ⭐开闭原则
      • (OCP) Open Closed Principle
      • Code
    • ⭐依赖倒置原则
      • (DIP) Dependency Inversion Principle
      • Code
    • ⭐接口隔离原则
      • (ISP) Interface Segregation Principle
      • Code
    • ⭐迪米特法则
      • (LOD) Law of Demeter
      • 无具体Code
    • ⭐合成复用原则
      • (CRP) Composite Reuse Principle
      • Code
  • END
    • 设计模式 李建忠 C++
    • 敏捷软件开发 - 面向对象设计的原则

前言

申明:

原视频:

面向对象-软件设计原则-1小时搞懂-波波酱老师_哔哩哔哩_bilibili

本文为up主的视频教学总结成文本和code

业主要是为了Cpper学习者学习。因为up在视频中使用的是java描述。

基本概念

  1. 📌可维护性质

    在不破坏原有代码设计,不要引入新bug的情况下,能够快速修改或者添加代码

    生活案例:比如一个iPhone在维修摄像头的时候,如果一个手抖,就可能呆滞喇叭或者麦克风损坏,从而影响了通讯或者音视频功能,因为它们都集成在一个电路板上。但是,单反在维修的时候,就不存在这种情况‘

  2. 📌可扩展性

    在不修改或者少量修改原有代码的情况下,可以通过扩展的方式添加新的功能代码

    生活案例:中秋到了,拿着iPhone想要拍个月亮发朋友圈,但是不管怎么拍,效果都不好。这个时候,只见隔壁老王把单发装在三脚架上,然后换上长焦镜头,“咔嚓”一声,我凑过去一看,“哇,真的是又大又圆啊!”。这个时候,单反可以根据不同的拍摄场景,扩展不同的镜头。

  3. 📌可复用性

    尽量减少代码的重复编写,直接复用已有的代码

    生活案例:开发教务系统的时候,直接复用权限管理模块

  4. 📌内聚性

    模块内部元素的紧密程度,内聚性越高,那么模块独立性越好,可维护性,复用性也越高

  5. 📌耦合性

    模块与模块之间的关联关系,耦合度越高,那么模块与模块之间的关联越复杂,那么可维护性,复用性越差

Design Principles

⭐单一职责原则

(SRP) Single Responsibility Principle

一个类或者模块只负责完成一个职责(或者功能)。

通俗的讲,如果这个类包含两个或者多个不相干的功能,那么这个类的职责就不够单一,应该将它拆分成多个功能更加单一,粒度更细的类

单一职责原则是实现高内聚,低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实现经验。

Code

在这里插入图片描述

old

#include <iostream>
#include <string>class UserInfo {
private:long userID;std::string userName;std::string phone;std::string province;std::string city;std::string region;std::string detailAddress;public:void save() {std::cout << "save user information" << std::endl;}void saveAddress() {std::cout << "save address information" << std::endl;}
};

new

#include <iostream>
#include <list>
#include <string>class Address {
private:std::string city;std::string region;std::string detailAddress;public:void saveAddress() {std::cout << "save address information" << std::endl;}
};class UserInfo {
private:long userID;std::string userName;std::string phone;std::string province;std::list<Address> addressList;public:void save() {std::cout << "save user information" << std::endl;}
};

⭐里氏替换原则

(LSP) Liskov Substitution Principle

子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的瑞吉行为不变及正确性不被破快。

通俗理解:子类可以扩展父类的功能,但不改变父类原有的功能。

换句话说,子类继承父类时,除添加新的方法完成新功能外,尽量不要重写父类的方法,如果重写父类方法,程序运行会发生出错概率

如果一定要用多态,那么父类可以设计成抽象父类或者接口。

Code

在这里插入图片描述

old

#include <iostream>// 接口过于庞大
class PhoneFunction {
public:virtual void call() = 0;virtual void message() = 0;virtual void camera() = 0;
};class ApplePhone : public PhoneFunction {
public:virtual void call() override {std::cout << "Apple " << __func__ << std::endl;}virtual void message() override {std::cout << "Apple " << __func__ << std::endl;}virtual void camera() override {std::cout << "Apple " << __func__ << std::endl;}
};class OldPhone : public PhoneFunction {
public:virtual void call() override {std::cout << "Old " << __func__ << std::endl;}virtual void message() override {std::cout << "Old " << __func__ << std::endl;}virtual void camera() override {std::cout << "Old " << __func__ << std::endl;}
};

new

#include <iostream>// 接口粒度最小
struct Call {virtual void call() = 0;
};struct Message {virtual void message() = 0;
};struct Camera {virtual void camera() = 0;
};class ApplePhone : public Call, public Message, public Camera {
public:virtual void call() override {std::cout << "Apple " << __func__ << std::endl;}virtual void message() override {std::cout << "Apple " << __func__ << std::endl;}virtual void camera() override {std::cout << "Apple " << __func__ << std::endl;}
};class OldPhone : public Call, public Message {
public:virtual void call() override {std::cout << "Old " << __func__ << std::endl;}virtual void message() override {std::cout << "Old " << __func__ << std::endl;}
};

⭐开闭原则

(OCP) Open Closed Principle

对扩展开放,对修改关闭

在程序需要进行拓展的时候,不要去修改原有代码,实现一个热拔插的效果。

简而言之,是为了使程序的扩展性好,易于维护与升级。

想要达到这样的效果,我们需要使用接口和抽象类

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。

而软件中异变的细节可以从抽象派生的实现类来进行扩展,当软件需要发生变化时,只需要根据需求派生一个实现类来扩展就可以了。

Code

在这里插入图片描述

old

#include <ctime>
#include <iostream>
#include <string>class Equation {
protected:int leftNum;int rightNum;int result;std::string op;public:std::string toString() {return std::to_string(leftNum) + op + std::to_string(rightNum) + "=" +std::to_string(result);}int generateRandom(int min, int max) {return rand() % (max - min + 1) + min;}Equation generateEquation(std::string op) {leftNum = generateRandom(0, 100);rightNum = generateRandom(0, 100);if (op == "+") {result = leftNum + rightNum;} else if (op == "-") {result = leftNum - rightNum;}this->op = op;return *this;}
};int main() {srand(time(0));Equation equation = Equation().generateEquation("-");std::cout << equation.toString() << std::endl;
}

new

#include <ctime>
#include <iostream>
#include <string>class Equation {
protected:int leftNum;int rightNum;int result;std::string op;public:std::string toString() {return std::to_string(leftNum) + op + std::to_string(rightNum) + "=" +std::to_string(result);}int generateRandom(int min, int max) {return rand() % (max - min + 1) + min;}// 抽象方法// 注意,C++中,抽象基类无法实例化,因此无法直接返回`Equation`virtual Equation* generateEquation() = 0;
};class AddEquation : public Equation {
public:Equation* generateEquation() override {leftNum = generateRandom(0, 100);rightNum = generateRandom(0, 100);result = leftNum + rightNum;this->op = "+";return this;}
};class SubEquation : public Equation {
public:Equation* generateEquation() override {leftNum = generateRandom(0, 100);rightNum = generateRandom(0, 100);result = leftNum - rightNum;this->op = "-";return this;}
};int main() {srand(time(0));// 多态Equation* equation = (new SubEquation())->generateEquation();std::cout << equation->toString() << std::endl;delete equation;equation = (new AddEquation())->generateEquation();std::cout << equation->toString() << std::endl;delete equation;
}

⭐依赖倒置原则

(DIP) Dependency Inversion Principle

模块之间要依赖抽象,不依赖实现,要面向接口编程,不要面向实现编程

高层模块不应该直接依赖底层模块,这样就降低了客户端与实现模块间的耦合。

Code

在这里插入图片描述

old

#include <iostream>class IntelCpu {
public:void calculate() {std::cout << "IntelCpu " << __func__ << std::endl;}
};class IntelMemory {
public:void storage() {std::cout << "IntelMemory " << __func__ << std::endl;}
};class Computer {
private:IntelCpu intelCpu;IntelMemory intelMemory;public:Computer() {}Computer(IntelCpu intelCpu, IntelMemory intelMemory) {this->intelCpu = intelCpu;this->intelMemory = intelMemory;}void startRun() {intelCpu.calculate();intelMemory.storage();}
};int main() {IntelCpu intelCpu;IntelMemory intelMemory;Computer computer(intelCpu, intelMemory);computer.startRun();
}

在这里插入图片描述

new

#include <iostream>class Cup {
public:virtual void calculate() = 0;
};class Memory {
public:virtual void storage() = 0;
};class IntelCpu : public Cup {
public:void calculate() override {std::cout << "IntelCpu " << __func__ << std::endl;}
};class IntelMemory : public Memory {
public:void storage() override {std::cout << "IntelMemory " << __func__ << std::endl;}
};class AmdCpu : public Cup {
public:void calculate() override {std::cout << "AmdCpu " << __func__ << std::endl;}
};class AmdMemory : public Memory {
public:void storage() override {std::cout << "AmdMemory " << __func__ << std::endl;}
};class Computer {
private:Cup* cpu;Memory* memory;public:Computer() {}Computer(Cup* cpu, Memory* memory) {this->cpu = cpu;this->memory = memory;}void startRun() {cpu->calculate();memory->storage();}
};int main() {Computer computer;IntelCpu intelCpu;IntelMemory intelMemory;computer = Computer(&intelCpu, &intelMemory);computer.startRun();AmdCpu amdCpu;AmdMemory amdMemory;computer = Computer(&amdCpu, &amdMemory);computer.startRun();
}

⭐接口隔离原则

(ISP) Interface Segregation Principle

客户端不应该被迫依赖于它不适用的方法,一个类对于另一个类的依赖应该建立在最小的接口上。

一个类实现一个接口,就必须实现这个接口的所有抽象方法,如果接口的设计过于庞大的话,实现类就被迫实现不需要的抽象方法。

Code

在这里插入图片描述

old

#include <iostream>class Bird {
protected:double runSpeed;double flySpeed;public:virtual void setRunSpeed(double runSpeed) {this->runSpeed = runSpeed;}virtual void setFlySpeed(double flySpeed) {this->flySpeed = flySpeed;}double calcFlyTime(double distance) {return distance / flySpeed;}double calcRunTime(double distance) {return distance / runSpeed;}
};class Parrot : public Bird {
public:void studySpeak() {std::cout << __func__ << std::endl;}
};class Ostrich : public Bird {
public:virtual void setFlySpeed(double flySpeed) override {this->flySpeed = 0;}
};int main() {Bird* parrot = new Parrot();parrot->setFlySpeed(150.0);std::cout << "fly 300km" << std::endl;std::cout << "parrot uses " << parrot->calcFlyTime(300.0) << " hours"<< std::endl;Bird* ostrich = new Ostrich();ostrich->setFlySpeed(150.0);std::cout << "fly 300km" << std::endl;std::cout << "ostrich uses " << ostrich->calcFlyTime(300.0) << " hours"<< std::endl;
}

在这里插入图片描述

new

#include <iostream>class Bird {
protected:double runSpeed;double flySpeed;public:virtual void setRunSpeed(double runSpeed) {this->runSpeed = runSpeed;}virtual void setFlySpeed(double flySpeed) {this->flySpeed = flySpeed;}double calcFlyTime(double distance) {return distance / flySpeed;}double calcRunTime(double distance) {return distance / runSpeed;}
};class Parrot : public Bird {
public:void studySpeak() {std::cout << __func__ << std::endl;}
};class Ostrich : public Bird {
public:virtual void setFlySpeed(double flySpeed) override {this->flySpeed = 0;}
};int main() {Bird* parrot = new Parrot();parrot->setFlySpeed(150.0);std::cout << "fly 300km" << std::endl;std::cout << "parrot uses " << parrot->calcFlyTime(300.0) << " hours"<< std::endl;Bird* ostrich = new Ostrich();ostrich->setFlySpeed(150.0);std::cout << "fly 300km" << std::endl;std::cout << "ostrich uses " << ostrich->calcFlyTime(300.0) << " hours"<< std::endl;
}

⭐迪米特法则

(LOD) Law of Demeter

迪米特法则来自于1987年美国东北大学的一个名为Demeter的一个项目,只跟朋友联系,不跟“陌生人”说话

如果两个软件实体无须直接通信,那么就不应该发生直接的互相调用,可以通过第三方转发该调用。

其目的是降低类之间的耦合度,提高模块的相对独立性。

无具体Code

无具体code

这个发展在项目中的各个模块调用非常之多,需要多项目,业务非常熟悉才能搭建良好的结构。

在这里插入图片描述

在这里插入图片描述

⭐合成复用原则

(CRP) Composite Reuse Principle

尽量先适用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

通常类的复用分为继承复用和合成复用两种。

继承复用虽然简单和易实现的优点,但它也存在以下缺点:

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为”白箱“复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展和维护。

采用组合或聚合复用时,可以将已有的对象纳入新对象中,使之成功新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:

  1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称”黑箱“复用。
  2. 对象间的耦合度低。可以在类的成员位置声明抽象(抽象类或者接口

Code

在这里插入图片描述

在这里插入图片描述

old

#include <iostream>
#include <string>class A {
protected:std::string name;int age;public:void methodA() {std::cout << "A " << __func__ << std::endl;}
};class B : public A {
public:void methodB() {std::cout << "B " << __func__ << std::endl;}
};int main() {B b;b.methodA();b.methodB();
}

new

#include <iostream>
#include <string>class A {
protected:std::string name;int age;public:void methodA() {std::cout << "A " << __func__ << std::endl;}
};class B : public A {
public:void methodB() {std::cout << "B " << __func__ << std::endl;}
};int main() {B b;b.methodA();b.methodB();
}



END

设计模式 李建忠 C++

设计模式 李建忠 C++

敏捷软件开发 - 面向对象设计的原则

在敏捷软件开发中提出了以下设计原则

SRP 单一职责原则

就一个类而言,应该仅有一个引起它变化的原因

OCP 开放封闭原则

软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改

LSP Liskov替换原则

子类型必须能替换换他们的基本类型

DIP 依赖倒置原则

抽象不应该依赖细节。细节应该依赖于抽象。

ISP 接口隔离原则

不应该强迫客户依赖于他们不用的方法。接口属于客户,不属于他所在的类层次结构。

REP 重用发布等价原则

重用的粒度就是发布的粒度

CCP 共用重用原则

一个包中的所有类应该是共用重用的。如果重用了包中的一个类,那么就重用包中的所有类。互相之间没有紧密联系的类不应该在同一个包中。

CRP 共用封闭原则

一个包中的所有类对于同一个类性质的变化应该是共同封闭的。一个变化若对一个包影响,则将对包中的所有类产生影响,而对其他的包不造成任何影响。

ADP 无依赖原则

在包的依赖关系中不存在环。细节不应该被依赖。

SDP 稳定依赖原则

朝着稳定的方向依赖。

ASP 稳定抽象原则

一个包的抽象程度应该和其他稳定程度一致。

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

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

相关文章

多头注意力机制

1、什么是多头注意力机制 从多头注意力的结构图中&#xff0c;貌似这个所谓的多个头就是指多组线性变换&#xff0c;但是并不是&#xff0c;只使用了一组线性变换层&#xff0c;即三个变换张量对 Q、K、V 分别进行线性变换&#xff0c;这些变化不会改变原有张量的尺寸&#xf…

openGauss学习笔记-93 openGauss 数据库管理-访问外部数据库-oracle_fdw

文章目录 openGauss学习笔记-93 openGauss 数据库管理-访问外部数据库-oracle_fdw93.1 编译oracle_fdw93.2 使用oracle_fdw93.3 常见问题93.4 注意事项 openGauss学习笔记-93 openGauss 数据库管理-访问外部数据库-oracle_fdw openGauss的fdw实现的功能是各个openGauss数据库及…

极坐标系下的交换积分次序

极坐标系下的交换积分次序 我把极坐标系下的交换积分次序总结为动静与静动之间的转换&#xff0c;下面通过一个例子感受一下 ρ 1 、 ρ 1 cos ⁡ θ \rho1、\rho1\cos\theta ρ1、ρ1cosθ ∫ 0 π / 2 d θ ∫ 1 1 cos ⁡ θ f ( ρ cos ⁡ θ , ρ sin ⁡ θ ) ρ d…

swoole进行性能查看火焰图tideways_xhprof xhgui

D:\dnmp\services\php\Dockerfile D:\dnmp\services\php\php.ini 在php的配置文件里面增加tideways_xhprof拓展&#xff1a; [xhprof] ;xhprof.output_dir /var/log/php/xhprof.logextensiontideways_xhprof.so在php配置文件里面加上xhgui的header&#xff1a; 这样就能开启…

C (1094) : DS双向链表—前驱后继

Description 在双向链表中&#xff0c;A有一个指针指向了后继节点B&#xff0c;同时&#xff0c;B又有一个指向前驱节点A的指针。这样不仅能从链表头节点的位置遍历整个链表所有节点&#xff0c;也能从链表尾节点开始遍历所有节点。 对于给定的一列数据&#xff0c;按照给定的…

flutter开发实战-inappwebview实现flutter与Javascript方法调用

flutter开发实战-inappwebview实现flutter与Javascript方法调用 在使用inappwebview时候&#xff0c;需要flutter端与JS进行交互&#xff0c;调用相应的方法&#xff0c;在inappwebview中的JavaScript Handlers。 一、JavaScript Handlers 要添加JavaScript Handlers&#…

Day4:Linux系统编程1-60P

我的学习方法是&#xff1a;Linux系统编程&#xff08;看pdf笔记&#xff09; Linux网络编程 WebServer 01P-17P Linux相关命令及操作 cp -a dirname1 dirname2 复制目录 cp -r dirname1 dirname2 递归复制目录 1 到目录 2 这里-a 和-r 的差别在于&#xff0c;-a 是完全复制…

mysql-执行计划

1. 执行计划表概述 id相同表示加载表的顺序是从上到下。 id不同id值越大&#xff0c;优先级越高&#xff0c;越先被执行。id有相同&#xff0c;也有不同&#xff0c;同时存在。 id相同的可以认为是一组&#xff0c;从上往下顺序执行&#xff1b;在所有的组中&#xff0c;id的值…

MYSQL06高级_为什么使用索引、优缺点、索引的设计、方案、聚簇索引、联合索引、注意事项

文章目录 ①. 为什么使用索引②. 索引及其优缺点③. InnoDb - 索引的设计④. InnoDb中的索引方案⑤. 索引 - 聚簇索引⑥. 索引 - 二级索引⑦. B树索引的注意事项⑧. MyISAM中索引方案 ①. 为什么使用索引 ①. 索引是存储引擎用于快速找到数据记录的一种数据结构,就好比去图书馆…

WebGoat 靶场 JWT tokens 四 五 七关通关教程

文章目录 webGoat靶场第 四 关 修改投票数第五关第七关 你购买书&#xff0c;让Tom用户付钱 webGoat靶场 越权漏洞 将webgoat-server-8.1.0.jar复制到kali虚拟机中 sudo java -jar webgoat-server-8.1.0.jar --server.port8888解释&#xff1a; java&#xff1a;这是用于执行…

React18学习

17、React_JSX的注意事项 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>JSX的注意</title><script src"./script/react.development.js"></script><script src"…

Mysql 8手动终止某个事务并释放其持有的锁

示范数据表 age具有index普通索引 在mysql数据库里的information_schema.INNODB_TRX表中存储有innodb的所有事务&#xff0c;我们可以查看该表来查看正在进行的事务 现在我开启一个事务&#xff0c;执行第1、2行SQL&#xff0c;启动事务并持有id3的行锁 刷新事务表可以看到…