软件设计原则 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.mzph.cn/news/98110.shtml

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

相关文章

【Oracle】Oracle系列十九--Oracle的体系结构

文章目录 往期回顾前言1. 物理结构2. 内存结构2.1 SGA2.2 后台进程 3. 逻辑结构 往期回顾 【Oracle】Oracle系列之一–Oracle数据类型 【Oracle】Oracle系列之二–Oracle数据字典 【Oracle】Oracle系列之三–Oracle字符集 【Oracle】Oracle系列之四–用户管理 【Oracle】Or…

基于springboot实现人职匹配推荐管理系统演示【项目源码+论文说明】分享

基于springboot实现人职匹配推荐管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于人职匹配推荐系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了…

ADuM1250 ADuM1251 模块 I2C IIC总线2500V电磁隔离 接口保护

功能说明&#xff1a; 1&#xff0c;2500V电磁隔离&#xff0c;2通道双向I2C&#xff1b; 2&#xff0c;支持电压在3到5.5V&#xff0c;最大时钟频率可达1000KHz&#xff1b; 3&#xff0c;将该隔离模块接入总线&#xff0c;可以保护主MCU引脚&#xff0c;降低I2C总线上的干…

【Java 进阶篇】HTML表格标签详解

HTML&#xff08;Hypertext Markup Language&#xff09;表格标签是在网页中用于创建表格的重要工具。表格是一种在网页上以行和列的方式组织和显示数据的有效方式。在本文中&#xff0c;我们将详细介绍HTML表格标签&#xff0c;包括如何创建表格、定义表头、单元格合并等内容。…

应用案例 | dataFEED OPC Suite为化工行业中的质量控制和成本节约提供数据集成方案

一 背景 在当今化工行业中&#xff0c;质量控制对于特种塑料供应商至关重要。一家国际性的特种塑料供应商在全球拥有五个生产基地&#xff0c;每个基地都运行着2-6台塑料挤出机。为了确保塑料质量&#xff0c;他们需要每两小时分析一次挤出样品——导致这项工作占用了较大的生…

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;这是用于执行…

京东数据分析平台:2023年8月京东奶粉行业品牌销售排行榜

鲸参谋监测的京东平台8月份奶粉市场销售数据已出炉&#xff01; 鲸参谋数据显示&#xff0c;8月份京东平台上奶粉的销售量将近700万件&#xff0c;环比增长约15%&#xff0c;同比则下滑约19%&#xff1b;销售额将近23亿元&#xff0c;环比增长约4%&#xff0c;同比则下滑约3%。…

【React】深入理解React组件状态State

目录 一、何为State二、如何定义State三、如何判断是否为State四、如何正确使用State1、用setState修改State2、State的更新是异步的①、代码示例 3、State更新会被合并①、组件状态例子②、当只需要修改状态title时&#xff0c;只需要将修改后的title传给setState③、React会合…

opencv图像的直方图,二维直方图,直方图均衡化

文章目录 opencv图像的直方图&#xff0c;二维直方图&#xff0c;直方图均衡化一、图像的直方图1、什么是图像的直方图&#xff1a;2、直方图的作用&#xff1a;3、如何绘制图像的直方图&#xff1a;&#xff08;1&#xff09;cv::calcHist()函数原型&#xff1a;英文单词 calc…

协议栈——创建套接字

前面几篇文章讲解的是应用程序使用Socket间接通知协议栈进行的连接&#xff0c;通信阶段&#xff0c;那么从现在开始讲解协议栈和网卡驱动的故事 回顾上篇文章&#xff0c;我们从第一阶段创建套接字&#xff0c;协议栈返回描述符讲起~~~ 协议栈结构 首先来看下协议栈的大致结…

计算机视觉: 基于隐式BRDF自编码器的文生三维技术

论文链接: MATLABER: Material-Aware Text-to-3D via LAtent BRDF auto-EncodeR 背景 得益扩散模型和大量的text - image 成对的图片&#xff0c; 现在文生2D的模型已经比较成熟的框架和模型&#xff0c;主流的技术比如说stable diffusion 和 midjourney 以及工业领域runway 等…

计算机毕设 招聘网站爬取与大数据分析可视化 - python 分析 可视化 flask

文章目录 0 前言1 课题背景2 实现效果3 Flask框架4 Echarts5 爬虫6 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自…

制作长图海报的详细指南,制作长图海报的5个步骤

制作长图海报是宣传活动、产品或服务的重要方式之一。乔拓云后台提供了丰富的海报模板&#xff0c;让你轻松制作出专业级的长图海报。下面将介绍如何使用乔拓云后台制作长图海报的技巧。 一、选择模板 首先&#xff0c;注册并登录乔拓云后台&#xff0c;进入云设计页面。在选择…

A (1087) : DS单链表--类实现

Description 用C语言和类实现单链表&#xff0c;含头结点 属性包括&#xff1a;data数据域、next指针域 操作包括&#xff1a;插入、删除、查找 注意&#xff1a;单链表不是数组&#xff0c;所以位置从1开始对应首结点&#xff0c;头结点不放数据 类定义参考 #include<…

Qt_基础

目录 1概述1.1 什么是QT1.2 QT的发展史1.3 支持的平台1.4 QT版本1.5 下载与安装1.6 QT的优点1.7 成功案例 2 创建 Qt 项目2.1 使用向导创建2.2 .pro文件2.3 帮助文档(QTcreator自带的)2.4 QT应用程序介绍 3 创建第一个小程序3.1 按钮的创建3.1.1 设置主窗口标题的函数3.1.2 **固…

小谈设计模式(23)—桥接模式

小谈设计模式&#xff08;23&#xff09;—桥接模式 专栏介绍专栏地址专栏介绍 桥接模式主要角色抽象部分实现部分分析 核心思想应用场景123 优缺点分析优点123 缺点12 总结 专栏介绍 专栏地址 link 专栏介绍 主要对目前市面上常见的23种设计模式进行逐一分析和总结&#x…

三极管及继电器的使用(单片机如何控制灯泡等大型电器)

1.对于初入硬件的小伙伴一定会用到三极管和继电器&#xff0c;如下图&#xff08;三极管&#xff09; 如下图&#xff08;继电器&#xff09; 当然上述三极管和继电器&#xff0c;只是众多的其中一种&#xff0c;而且继电器是包装好了的&#xff0c;这个应该叫继电器模块&#…

SpringBoot 实现数据脱敏

SpringBoot 实现数据脱敏 前言Hutool 实现数据脱敏引入依赖脱敏工具类代码实现 使用注解的方式定义枚举自定义序列化类定义注解测试 前言 数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形&#xff0c;实现敏感隐私数据的可靠保护。 数据脱敏常用规则有替换、重排、加密…

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

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

leetCode 1143.最长公共子序列 动态规划

1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串…