软件设计原则 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.rhkb.cn/news/151519.html

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

相关文章

【抢先体验】开通使用 ChatGPT 语音版功能保姆级教程

大家好&#xff0c;我是苍何&#xff0c;一个土木转码的非典型程序员&#xff0c;也是一名技术管理者&#xff0c;同时也是 AI 应用的探索者。今天在视频号上看到和 ChatGPT 语音对话的视频&#xff0c;其声音的真实感太让人震撼了&#xff0c;于是也想去抢先体验一下 ChatGPT …

Centos7安装MongoDB7.xxNoSQL数据库|设置开机启动(骨灰级+保姆级)

一: mongodb下载 MongoDB 社区免费下载版 MongoDB社区下载版 [rootwww tools]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.1.0-rc4.tgz 二: 解压到指定目录 [rootwww tools]# mkdir -p /usr/local/mongodb [rootwww tools]# tar -zxvf mongodb-…

选择适合普通公司的项目管理软件

不管是打工人还是学生党都适合使用Zoho Projects项目管理软件。利用项目概览功能&#xff0c;将整体项目尽收眼底&#xff0c;作为项目管理者&#xff0c;项目日程、进度都可见&#xff0c;Zoho Projects项目管理APP助推项目每一环节的进展&#xff0c;更便于管理者设计项目的下…

ThingsBoard如何自定义tcp-transport

1、概述 很久没有更新了,一直忙于其他的事情,最近去搞了一个在ThingsBoard中自定义一个tcp-transport,用于连接使用tcp长连接的设备,目前使用tcp和mqtt协议连接服务端的设备还是很多,ThingsBoard的PE版提供了Integration是可以实现tcp的接入,但是CE版是没有提供接入tcp长…

【MySQL】基本查询(二)

文章目录 一. 结果排序二. 筛选分页结果三. Update四. Delete五. 截断表六. 插入查询结果结束语 操作如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chi…

IO 之 操作properties属性文件

propreties文件&#xff1a; properties文件是一种用于存储配置信息的文本文件&#xff0c;通常以“.properties”为文件扩展名。它是一种简单的键值对格式&#xff0c;用于保存应用程序的配置参数。 在properties文件中&#xff0c;每一行都包含一个键值对&#xff0c;键和值…

HTTPS工作过程,国家为什么让http为什么要换成https,Tomcat在MAC M1电脑如何安装,Tomcat的详细介绍

目录 引言 一、HTTPS工作过程 二、Tomcat 在访达中找到下载好的Tomcat文件夹&#xff08;这个要求按顺序&#xff09; zsh: permission denied TOMCAT的各部分含义&#xff1a; 引言 在密码中一般是&#xff1a;明文密钥->密文&#xff08;加密&#xff09; &#xff…

Spring源码解析——IOC属性填充

正文 doCreateBean() 主要用于完成 bean 的创建和初始化工作&#xff0c;我们可以将其分为四个过程&#xff1a; 最全面的Java面试网站 createBeanInstance() 实例化 beanpopulateBean() 属性填充循环依赖的处理initializeBean() 初始化 bean 第一个过程实例化 bean在前面一篇…

复旦大学EMBA:揭秘科创企业,领略未来战略!

智能制造&#xff0c;国之重器。作为制造强国建设的主攻方向&#xff0c;智能制造的发展水平关系到我国未来制造业在全球的地位与影响力。发展智能制造&#xff0c;是加快建设现代化产业体系的重要手段&#xff0c;提升供给体系适配性的有力抓手&#xff0c;也是建设数字中国的…

【C++设计模式之状态模式:行为型】分析及示例

简介 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;看起来就像是改变了其类。状态模式将对象的状态封装成不同的类&#xff0c;并使得对象在不同状态下有不同的行为。 描述 状态模式通过…

Android用户登录与数据存储:从权限请求到内外部存储的完整实践【完整实践步骤、外部存储、内部存储】

步骤 1: 登录页面布局 在 MainActivity 中实现用户登录功能&#xff0c;首先创建一个布局文件 activity_main.xml 包含用户名和密码的输入字段以及登录按钮。 <!-- activity_main.xml --> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/andr…

Qt之实现圆形进度条

在Qt自带的控件中&#xff0c;只有垂直进度条、水平进度条两种。 在平时做页面开发时&#xff0c;有些时候会用到圆形进度条&#xff0c;比如说&#xff1a;下载某个文件的下载进度。 展示效果&#xff0c;如下图所示&#xff1a; 实现这个功能主要由以下几个重点&#xff1a…

记录vue开发实例

封装的表格组件 <template><div><div style"width: 100%" v-if"showList"><el-table v-loading.lock"loading" :data"dataList":header-cell-style"{background: #F2FCFE,fontSize: 14px,color: #50606D}&…

因为在此系统上禁止运行脚本

问题&#xff1a; 解决办法&#xff1a; vue项目搭建中"因为在此系统上禁止运行脚本"报错&#xff0c;解决方法 - 你的剧本 - 博客园 (cnblogs.com)

详解链表oJ<反转链表,链表的中间节点及链表的回文>

hello&#xff0c;大家好&#xff0c;这里是Dark FlameMaster,今天和大家分享的是有关数据结构链表的几道题目&#xff0c;链表的中间节点&#xff0c;反转链表及判断链表是否为回文结构&#xff0c;放在一起讲解会印象更加深刻。 文章目录 一&#xff0c;链表的中间节点二&…

从0到1基于ChatGLM-6B使用LoRA进行参数高效微调

从0到1基于ChatGLM-6B使用LoRA进行参数高效微调 吃果冻不吐果冻皮 ​ 关注他 cliniNLPer 等 189 人赞同了该文章 ​ 目录 收起 ChatGLM-6B简介 具备的一些能力 局限性 LoRA 技术原理 环境搭建 数据集准备 数据预处理 参数高效微调 单卡模式模型训练 数据并行模式模型训练 模型推…

自动驾驶学习笔记(二)——Apollo入门

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 Ubuntu Linux文件系统 Linux指令…

水波纹文字效果动画

效果展示 CSS 知识点 text-shadow 属性绘制立体文字clip-path 属性来绘制水波纹 工具网站 CSS clip-path maker 效果编辑器 页面整体结构实现 使用多个 H2 标签来实现水波纹的效果实现&#xff0c;然后使用clip-path结合动画属性一起来进行波浪的起伏动画实现。 <div …

5分钟理解什么是卷积的特征提取

大家好啊&#xff0c;我是董董灿。 卷积算法之所以重要&#xff0c;关键在于其提取特征的能力。 5分钟入门卷积算法中提到&#xff0c;卷积模仿的就是人眼识图的过程&#xff0c;以“感受野”的视角去扫描图片&#xff0c;从而获取不同区域的图片信息。 在这一过程中&#x…

新风机注意事项有哪些?

选择和使用新风机时&#xff0c;有几个关键注意事项需要牢记&#xff1a; 安装位置&#xff1a;新风机的安装位置很重要。通常情况下&#xff0c;应将其安装在室外以避免室内产生噪音和减少室内的体积占据。确保选择合适的安装位置&#xff0c;以便新风机能够顺利引入新鲜空气。…