C++设计模式_02_面向对象设计原则

文章目录

  • 1. 面向对象设计,为什么?
  • 2. 重新认识面向对象
  • 3. 面向对象设计原则
    • 3.1 依赖倒置原则(DIP)
    • 3.2 开放封闭原则(OCP )
    • 3.3 单一职责原则( SRP )
    • 3.4 Liskov 替换原则 ( LSP )
    • 3.5 接口隔离原则 ( ISP )
    • 3.6 优先使用对象组合,而不是类继承
    • 3.7 封装变化点
    • 3.8 针对接口编程,而不是针对实现编程
  • 4. 将设计原则提升为设计经验

1. 面向对象设计,为什么?

变化是复用的天敌!面向对象设计或者说使用了抽象原则的面向对象设计最大的优势在于:
抵御变化!

2. 重新认识面向对象

之前我们对面向对象的理解,包括封装、继承、多态,这是属于面向对象的底层思维理解。从抽象思维进行如下理解:

  • 理解隔离变化
    • 从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小
  • 各司其职
    • 从微观层面来看,面向对象的方式更强调各个类的“责任”
    • 由于需求变化导致的新增类型不应该影响原来类型的实现-所谓各负其责(多态更多的使用)
  • 对象是什么?
    • 从语言实现层面来看,对象封装了代码和数据。
    • 从规格层面讲,对象是一系列可被使用的公共接口。
    • 从概念层面讲,对象是某种拥有责任的抽象

3. 面向对象设计原则

设计原则要比具体某一个模式更为重要,后期讲到的模式当下已经不流行,某些已经被有些语言模式替代,甚至忘了也不重要。有了这些设计原则可以理解其他的模式,甚至可以设计自己的模式。

有一种错误的观点就是将设计模式当做算法去学习,后期可以看到两段代码非常相似,但是表达的模式是完全不同的,也有一种可能就是代码千差万别,但是设计模式是一样的。

所有的设计模式都是依赖这里所讲的设计原则,如果违背了设计原则,这种设计模式就是错误的。设计原则是一把尺子,可以用来衡量软件设计品质。
学习设计模式,一定不要只看代码的调用流程,如果你了解虚函数的调用机制,23种设计模式可以很快看懂,但是要掌握设计模式,就需要从设计原则的方向出发。

3.1 依赖倒置原则(DIP)

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
  • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。

上面的话怎么理解呢,上篇中Mainform,依赖于Line和Rec。示意图如下图所示:箭头指向的代表了依赖
在这里插入图片描述
Mainform属于高层模块,Line和Rec属于底层模块,由于底层模块是容易变化的,高层模块是相当文档的,稳定的东西依赖于变化的东西,稳定的也就变成不稳定的,而应该将二者都依赖于抽象。

转换为第二种方式,示意图如下图所示:
在这里插入图片描述
Mainform依赖于Shape,Line和Rec也是依赖于Shape,Shape也就是上面讲到的抽象。

有些人喜欢在抽象类中使用子类,那就违背了抽象(稳定)不应该依赖于实现细节(变化) ,这是因为抽象应该是稳定的,如果依赖于一个实现细节,这个实现细节是容易变化的,就会变成,稳定的依赖于变化。

这里最关键的地方就是Line和Rec不稳定,容易变化,导致Mainform不稳定。

依赖倒置原则( DIP)原则贯穿于所有的设计模式,这种原则是十分依赖抽象类的。

3.2 开放封闭原则(OCP )

  • 对扩展开放,对更改封闭。
  • 类模块应该是可扩展的,但是不可修改。

首先举一个现实生活的例子,有一个需求,需要做一套桌椅板凳,将需求提交给一个木器加工厂,加工过程中,使用场景是在一个办公大楼,所有的办公家具需要达到一个几级阻燃的标准,现在将新的需求告诉加工厂,木器加工厂如何应对呢?有两种应对方法:

  1. 将做好的东西扔掉,重新再弄,这是一种改变的做法,不太好,浪费程度很大。
  2. 聪明的木器加工厂,只需要刷一层防火涂料就可以了,他已经做了一些工作,只需要在前面基础上刷一层防火涂料,这是一种扩展的做法。

从代码的角度分析,上篇的代码中就使用了开放封闭原则,第二种方法就是以扩展的方式来应对需求的变化。

当需求变化的情况下,不要想着去更改,应该想着增加一些东西来应对需求的变化。

3.3 单一职责原则( SRP )

  • 一个类应该仅有一个引起它变化的原因。
  • 变化的方向隐含着类的责任

有的时候,你会发现一个类会写的很臃肿,当一个类中放了太多的功能,也就隐含了多个责任,当隐含多个责任的时候就会出毛病,多个责任将你往不同的方向拉扯,用子类的时候,一会这个方向,一会那个方向。后期会讲到桥模式装饰模式都会遇到类的责任问题,类的责任问题通常情况下,表现不是那么强烈,当遇到特殊的场合,类的责任是非常重要。

3.4 Liskov 替换原则 ( LSP )

  • 子类必须能够替换它们的基类(IS-A)
  • 继承表达类型抽象。

作为子类,所有需要父类的地方,都可以将子类传过去进行使用,这是天经地义的,但是具体实现时,就是会有人打破这个原则。
例如,有些程序员朋友喜欢在继承父类的子类中,发现父类中的三个方法,子类无法使用的,这个时候会在子类的3个方法中throw exception,这个做法就不对了。这就说明子类没有继承父类,或者说有可能是组合关系。

3.5 接口隔离原则 ( ISP )

  • 不应该强迫客户程序依赖它们不用的方法。
  • 接口应该小而完备。

“接口应该小而完备”中的意思就是,不要把不必要的方法,public出去。如果只是子类使用的话,就protected。如果本类使用的话就private。如果真正有必要暴露的情况下,才将其public出去。
当你无节制的public出去,就很容易让外部的客户程序对其产生依赖,一旦产生依赖,接口就需要保持稳定。

3.6 优先使用对象组合,而不是类继承

  • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用” 。
  • 继承在某种程度上破坏了封装性,子类父类耦合度高。
  • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

更准确的继承关系,其实是一种类属关系,比如说动物继承自生物,人又继承自动物。很多时候我们会错用继承,通常我们会有另外一种表白方法:对象组合(class A中包含class B,B可以是指针或者对象)。

3.7 封装变化点

  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。

当设计的素养逐步提升,是可以在软件领域划分分界层,从传统面向对象思维,封装就是封装代码和数据,但是封装更高层次就是封装变化点,一侧变化,一侧稳定。

3.8 针对接口编程,而不是针对实现编程

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口。(针对业务类型,而不是像string等的具体类)
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
  • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案。

上篇中:

	vector<Line> lineVector;
	vector<Rect> rectVector;

就违背了针对接口编程的原则,而

vector<Shape*> shapeVector;  //放抽象接口
......
shapeVector[i]->Draw(e.Graphics);  //使用接口的通用方法即可

针对接口编程的原则通常是与依赖倒置原则(DIP)相辅相成,违背了一个,通常也就违背了另一个。

不同设计原则是从不同角度看问题的。

面向接口设计的深入思考:产业强盛的标准:接口标准化!

接口的标准化,也就代表了工作的分配

4. 将设计原则提升为设计经验

  • 设计习语 Design Idioms
    • Design Idioms 描述与特定编程语言相关的低层模式,技巧,惯用法。
  • 设计模式 Design Patterns
    • Design Patterns主要描述的是“类与相互通信的对象之间的组织关系,包括它们的角色、职责、协作方式等方面。
  • 架构模式 Architectural Patterns
    • Architectural Patterns描述系统中与基本结构组织关系密切的高层模式,包括子系统划分,职责,以及如何组织它们之间关系的规则。

下篇将会真正进入每一个模式,进入某一个模式之前,需要深入领会上面提到8大设计原则,只有这样才能灵活的运用某一种模式。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法日志】动态规划刷题:股票买卖问题(day41)

代码随想录刷题60Day 目录 前言 买卖股票的最佳时机1 买卖股票的最佳时机2 买卖股票的最佳时机3 买卖股票的最佳时机4 前言 本日着重于多状态问题的处理&#xff0c;各状态之间会有一定联系&#xff0c;状态转移方程将不再局限一个。 买卖股票的最佳时机1 int maxProfit(…

RHCE——九、SELinux

SELinux 一、概念1、作用2、SELinux与传统的权限区别 二、SELinux工作原理1、名词解释主体&#xff08;Subject&#xff09;目标&#xff08;Object&#xff09;策略&#xff08;Policy&#xff09;安全上下文&#xff08;Security Context&#xff09; 2、文件安全上下文查看1…

[ZenTao]源码阅读:自定义任务类型

1、module/custom/control.php 2、module/custom/model.php

php开发环境搭建_宝塔、composer

宝塔面板下载&#xff0c;免费全能的服务器运维软件 一 下载宝塔面板 解压安装 登录之后修改安全入口 1 进入软件商店下载nginx,mysql5.6,php7.2 2 将php的安装路径配置到环境变量中 此电脑--右键--点击属性---高级系统设置---环境变量---系统变量path---添加确定 输入php -v…

音视频技术开发周刊 | 308

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 OpenAI首席科学家最新访谈&#xff1a;对模型创业两点建议、安全与对齐、Transformer够好吗&#xff1f; OpenAI首席科学家Ilya Sutskever最近和他的朋友Sven Strohband进…

大数据到底是好是坏?_光点科技

近年来&#xff0c;随着科技的不断发展和互联网的普及&#xff0c;大数据已经成为一个备受关注的话题。它带来了许多机遇和挑战&#xff0c;引发了人们对于其是好是坏的争议。大数据究竟是一把双刃剑&#xff0c;需要我们从多个角度来审视。 大数据的好处无疑是显而易见的。首先…

java入坑之网络编程

一、 网络基础知识 1.1网卡 1.2IP地址 1.3端口 1.4保留IP 1.5网络协议 二、UDP 编程 2.1相关概念 计算机通讯&#xff1a;数据从一个IP的port出发&#xff08;发送方&#xff09;&#xff0c;运输到另外一个IP的port&#xff08;接收方&#xff09; UDP&#xff1a;无连接无…

详解I2C

I2C&#xff08;也常写作 I I C IIC IIC&#xff0c; I 2 C I^2C I2C&#xff09;&#xff0c;全称为Inter-Integrated Circuit&#xff08;“互连集成电路”&#xff09;&#xff0c;用于在集成电路之间进行短距离数据传输。它由Philips&#xff08;现在的NXP半导体&#xff0…

DolphinDB 携手白鲸开源 WhaleStudio 打造高效敏捷的 DataOps 解决方案

浙江智臾科技有限公司&#xff08;简称&#xff1a;DolphinDB&#xff09;和北京白鲸开源科技有限公司&#xff08;简称&#xff1a;白鲸开源&#xff09;是在大数据技术领域活跃的两支专业团队。 DolphinDB 专注于为用户提供集高性能存储、复杂分析能力和流处理于一体的实时计…

Linux内核源码分析 (5)多处理器调度

Linux内核源码分析 (5)多处理器调度 文章目录 Linux内核源码分析 (5)多处理器调度注&#xff1a;本章节使用的内核版本为Linux 5.6.18一、 SMT和NUMA1、SMP (对称多处理器结构)2、NUMA &#xff08;非一致内存访问结构&#xff09; 二、多核调度三、调度域和调度组四、SMP调度详…

Power BI 连接 MySQL 数据库

Power Query 或 Power BI 只提供了对 SQL Server 的直接连接&#xff0c;而不支持其它数据库的直连。所以第一次连接 MySQL 数据库时&#xff0c;就出现下面的错误信。 这就需要我们自己去安装一个连接器组件。https://downloads.mysql.com/archives/c-net/ 错误解决方案 我一…

MySQL 8 数据清洗总结

MySQL 8 数据清洗三要素&#xff1a; 库表拷贝和数据备份数据清洗SQL数据清洗必杀技-存储过程 前提&#xff1a;数据库关联库表初始化和基础数据初始化&#xff1a; -- usc.t_project definitionCREATE TABLE t_project (id varchar(64) NOT NULL COMMENT 主键,tid varchar(…

Spring MVC 五 - Spring MVC的配置和DispatcherServlet初始化过程

今天的内容是SpringMVC的初始化过程&#xff0c;其实也就是DispatcherServilet的初始化过程。 Special Bean Types DispatcherServlet委托如下一些特殊的bean来处理请求、并渲染正确的返回。这些特殊的bean是Spring MVC框架管理的bean、按照Spring框架的约定处理相关请求&…

rtsp 拉流 gb28181 收流 经AI 算法 再生成 rtsp server (一)

1、 rtsp 工具 1 vlc 必备工具 2 wireshark 必备工具 3 自己制作的工具 player 使用tcp 拉流&#xff0c;不自己写的话&#xff0c;使用ffmpeg 去写一个播放器就行 4 live555 编译好live555&#xff0c; 将live555的参数修改以下&#xff0c;主要是缓存大小 文章使用c 来写一…

JavaScript Web APIs-01学习

复习&#xff1a; splice() 方法用于添加或删除数组中的元素。 **注意&#xff1a;**这种方法会改变原始数组。 删除数组&#xff1a; splice(起始位置&#xff0c; 删除的个数) 比如&#xff1a;1 let arr [red, green, blue] arr.splice(1,1) // 删除green元素 consol…

LinkedHashMap实现LRU缓存cache机制,Kotlin

LinkedHashMap实现LRU缓存cache机制&#xff0c;Kotlin LinkedHashMap的accessOrdertrue后&#xff0c;访问LinkedHashMap里面存储的元素&#xff0c;LinkedHashMap就会把该元素移动到最尾部。利用这一点&#xff0c;可以设置一个缓存的上限值&#xff0c;当存入的缓存数理超过…

取一个整数各偶数位上的数构成一个新的数字

1 题目 我可太难了&#xff0c;这题我的思路有点复杂&#xff0c;遇到的困难很多&#xff0c;总是值传递搞不清楚&#xff0c;地址传递总是写错。 从低位开始取出一个整数s的各奇数位上的数&#xff0c;剩下的偶数位的数依次构成一个新数t。 例如&#xff1a; 输入s&#xff…

vue自定义键盘

<template><div class"mark" click"isOver"></div><div class"mycar"><div class"mycar_list"><div class"mycar_list_con"><p class"mycar_list_p">车牌号</p>…

浪潮云海护航省联社金融上云,“一云多芯”赋能数字农业

农村金融是现代金融体系的重要组成部分&#xff0c;是农业农村发展的重要支撑力量&#xff0c;而统管全省农商行及农信社的省级农村信用社联合社&#xff08;以下简称&#xff1a;省联社&#xff09;在我国金融系统中占据着举足轻重的地位。省联社通常采用“大平台小法人”的发…

稳定性建设框架 | 京东物流技术团队

一、为什么要做稳定性建设 1、从熵增定律引出稳定性建设的必要性 物理学上&#xff0c;用“熵”来描述一个体系的混乱程度。卡尔弗里德曼提出熵增定律&#xff0c;他认为在一个封闭的系统内&#xff0c;如果没有外力的作用&#xff0c;一切物质都会从有序状态向无序状态发展。…