面向对象 设计原则

0 引言

 单一职责原则:类应该只有一个改变的理由;

 开放-封闭原则:类应该对扩展开放,对修改关闭;

迪米特原则:只和朋友交谈;

里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能;

接口隔离原则:不能强迫用户去依赖那些他们不使用的接口;

依赖倒转原则:针对接口编程,不针对实现编程;

组合/聚合复用原则:多用组合,少用继承。

1 单一职责原则(SRP)

它的准确解释是,就一个类而言,应该仅有一个引起它变化的原因[ASD]。即永远不要让一个类存在多个改变的理由。

如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,而这种变化将影响到该类不同职责的使用者(不同用户):

一方面,如果一个职责使用了外部类库,则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。
另一方面,某个用户由于某个原因需要修改其中一个职责,另外一个职责的用户也将受到影响,他将不得不重新编译和配置。这违反了设计的开闭原则,也不是我们所期望的。

如果你能想到一个类存在多个使其改变的原因,那么这个类就存在多个职责。

2 开放-封闭原则(OCP)

开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。[ASD]

扩展开放:某模块的功能是可扩展的,则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。
修改关闭:某模块被其他模块调用,如果该模块的源代码不允许修改,则该模块修改关闭的。软件系统的功能上的稳定性,持续性要求模块是修改关闭的。

原因:稳定性。开闭原则要求扩展功能不修改原来的代码,这可以让软件系统在变化中保持稳定。
扩展性。开闭原则要求对扩展开放,通过扩展提供新的或改变原有的功能,让软件系统具有灵活的可扩展性。遵循开闭原则的系统设计,可以让软件系统可复用,并且易于维护。

开闭原则的实现方法

为了满足开闭原则的对修改关闭原则以及扩展开放原则,应该对软件系统中的不变的部分加以抽象,在面向对象的设计中,

  1. 可以把这些不变的部分加以抽象成不变的接口,这些不变的接口可以应对未来的扩展;
  2. 接口的最小功能设计原则。根据这个原则,原有的接口要么可以应对未来的扩展;不足的部分可以通过定义新的接口来实现;
  3. 模块之间的调用通过抽象接口进行,这样即使实现层发生变化,也无需修改调用方的代码。

接口可以被复用,但接口的实现却不一定能被复用。
接口是稳定的,关闭的,但接口的实现是可变的,开放的。
可以通过对接口的不同实现以及类的继承行为等为系统增加新的或改变系统原来的功能,实现软件系统的柔性扩展。

3 迪米特原则(最少知道原则)LoP

迪米特法则(LoD),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。[J&DP]

迪米特法则的目的在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块相互独立,相互之间不存在依赖关系。

应用迪米特法则有可能造成的一个后果就是,系统中存在的大量的中介类,这些类只所以存在完全是为了传递类之间的相互调用关系—这在一定程度上增加系统的复杂度。

4 里氏替换原则(LSP)

所有引用基类的地方必须能透明地使用其派生类的对象。里氏替换原则是对开闭原则的扩展,它表明我们在创建基类的新的子类时,不应该改变基类的行为。

也就是说,只有满足以下2个条件的OO设计才可被认为是满足了LSP原则:

  • 不应该在代码中出现if/else之类对派生类类型进行判断的条件。
  • 派生类应当可以替换基类并出现在基类能够出现的任何地方,或者说如果我们把代码中使用基类的地方用它的派生类所代替,代码还能正常工作。

里式替换原则的引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。 

具体来说:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的方法时(重载/重写或实现抽象方法)的后置条件(即方法的输出/返回值)要比父类更严格或相等。

 

5  接口隔离原则(ISP)

 不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。

它包含了2层意思:

  • 接口的设计原则:接口的设计应该遵循最小接口原则,不要把用户不使用的方法塞进同一个接口里。如果一个接口的方法没有被使用到,则说明该接口过胖,应该将其分割成几个功能专一的接口。
  • 接口的依赖(继承)原则:如果一个接口a继承另一个接口b,则接口a相当于继承了接口b的方法,那么继承了接口b后的接口a也应该遵循上述原则:不应该包含用户不使用的方法。 反之,则说明接口a被b给污染了,应该重新设计它们的关系。

 如果用户被迫依赖他们不使用的接口,当接口发生改变时,他们也不得不跟着改变。换而言之,一个用户依赖了未使用但被其他用户使用的接口,当其他用户修改该接口时,依赖该接口的所有用户都将受到影响。这显然违反了开闭原则,也不是我们所期望的。

总而言之,接口分隔原则指导我们:

  • 一个类对一个类的依赖应该建立在最小的接口上
  • 建立单一接口,不要建立庞大臃肿的接口
  • 尽量细化接口,接口中的方法尽量少

 6 依赖倒转原则(DIP)

赖倒转原则,原话解释是抽象不应该依赖细节,细节应该依赖于抽象,这话绕口,说白了,就是要针对接口编程,不要对实现编程。

A. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
B. 抽象不应该依赖于细节,细节应该依赖于抽象

C.针对接口编程,不要针对实现编程。

在高层模块与低层模块之间,引入一个抽象接口层。

High Level Classes(高层模块) --> Abstraction Layer(抽象接口层) --> Low Level Classes(低层模块)

抽象接口是对低层模块的抽象,低层模块继承或实现该抽象接口。

这样,高层模块不直接依赖低层模块,而是依赖抽象接口层。抽象接口也不依赖低层模块的实现细节,而是低层模块依赖(继承或实现)抽象接口。

7 组合/聚合复用原则即组合优于继承

尽量使用组合/聚合,不要使用类继承。

聚合表示整体与部分的关系,表示“含有”,整体由部分组合而成,部分可以脱离整体作为一个独立的个体存在。组合则是一种更强的聚合,部分组成整体,而且不可分割,部分不能脱离整体而单独存在。在合成关系中,部分和整体的生命周期一样,组合的新的对象完全支配其组成部分,包括他们的创建和销毁。

只有当以下的条件全部被满足时,才应当使用继承关系:

1)派生类是基类的一个特殊种类,而不是基类的一个角色,也就是区分"Has-A"和"Is-A"。只有"Is-A"关系才符合继承关系,"Has-A"关系应当用聚合来描述。

2)永远不会出现需要将派生类换成另外一个类的派生类的情况。如果不能肯定将来是否会变成另外一个派生类的话,就不要使用继承。

3)派生类具有扩展基类的责任,而不是具有置换掉(override)或注销掉(Nullify)基类的责任。如果一个派生类需要大量的置换掉基类的行为,那么这个类就不应该是这个基类的派生类。

4)只有在分类学角度上有意义时,才可以使用继承。

参考:面向对象设计的七大设计原则详解_面向对象的八大设计原则-CSDN博客

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

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

相关文章

5.14 BCC工具之vfsreadlat.py解读

一,工具简介 vfsreadlat.py工具用于跟踪VFS读取延迟分布情况,示例的运行结果是一个函数延迟分布直方图。 二,代码示例 #!/usr/bin/python# USAGE: vfsreadlat.py [interval [count]] # # The default interval is 5 seconds. A Ctrl-C will print the partially # gathere…

RocketMQ—RocketMQ消息重复消费问题

RocketMQ—RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题: 生产者多次投递消息:如果生产者发送消息时,连接有延迟,MQ还没收到消息,生产者又发送了一次消息; 消费者方扩容…

「Python系列」Python函数

文章目录 一、Python函数函数的组成部分:调用函数:变量作用域:默认参数和可变参数: 二、匿名函数(Lambda函数)三、相关链接 一、Python函数 Python函数是组织代码的一种方式,它允许你定义可重用…

洛谷 8599.带分数

这道题其实是蓝桥杯的一道真题,其实这题看似很难解,但是如果稍加点化,其实很简单。 思路:因为我们需要选择1-9的数字来组成可以按照题目要求计算成目标数的形式,而且只有一次选用。那么,我们可以想&#x…

SEO优化对服务器有影响吗

SEO优化对服务器有影响吗 SEO优化也就是搜索引擎优化,简单的理解就是利用搜索引擎的规则来提高自己网站在搜索引擎上的自然排名。那么究竟服务器对于SEO优化是否有影响呢?SEO优化对服务器的选择有没有什么要求?服务器对SEO优化有什么影响&…

Docker命令实战

文章目录 一、Docker常用命令-图谱二、基础实战命令2.1、查找镜像2.2、启动容器2.3、修改容器内容2.3.1、进入容器内部修改2.3.2、挂载数据到外部修改 2.4、提交改变2.5、镜像传输--将镜像保存成压缩包2.6、两台主机间压缩文件的传输拷贝2.7、推送阿里云个人远程镜像仓库2.8、其…

【水文】计算并输出1到100之间所有质数的和

#include <stdio.h> #include <stdbool.h> // 函数声明 bool isPrime(int num); int main() { int sum 0; printf("1到100之间的质数有&#xff1a;\n"); for (int i 2; i < 100; i) { if (isPrime(i)) { printf("%…

CentOS7与Ubuntu防火墙管理命令操作总结

CentOS7与Ubuntu防火墙管理命令操作总结 CentOS7启动、停止和重启防火墙置默认区域查看防火墙状态和规则添加和删除规则重新加载和永久生效配置 Ubuntu防火墙管理操作安装和启用ufw验证ufw状态管理操作配置ufw规则删除和禁用规则 CentOS7 在CentOS7上&#xff0c;默认使用firew…

Nodejs 第三十九章(knex)

knex Knex是一个基于JavaScript的查询生成器&#xff0c;它允许你使用JavaScript代码来生成和执行SQL查询语句。它提供了一种简单和直观的方式来与关系型数据库进行交互&#xff0c;而无需直接编写SQL语句。你可以使用Knex定义表结构、执行查询、插入、更新和删除数据等操作。…

SQL110 插入记录(一)(插入和interval关键字的用法)

代码 insert into exam_record(uid,exam_id,start_time,submit_time,score) values(1001,9001,2021-09-01 22:11:12,2021-09-01 22:11:12interval 50 minute,90), (1002,9002,2021-09-04 07:01:02,null,null)知识点 interval关键字的用法 INTERVAL关键字一般使用格式为&#x…

Python爬虫之Splash负载均衡配置

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx Splash基础&#xff1a;Python爬虫之Splash详解-CSDN博客 用 Splash 做页面抓取时&#xff0c;如果爬取的量非常大&#xff0c;任务非常多&#xff0c;用一个 Splash 服务来处理的话&#xff0c;未免压力太大了&#xff0c;此…

【Linux 内核源码分析】内存映射(mmap)机制原理

内存映射(mmap)是 Linux 内核的一个重要机制&#xff0c;它为程序提供了一种将文件内容直接映射到进程虚拟地址空间的方式。同时内存映射也是虚拟内存管理和文件 IO 的重要组成部分。 在 Linux 中&#xff0c;虚拟内存管理是基于内存映射来实现的。在调用 mmap 函数时&#xf…

一篇博客教会你使用node多版本管理

文章目录 nvm 简介nvm 安装nvm 使用配置国内镜像显示可以安装的 nodejs安装 nodejs显示已安装的 nodejs切换 nodejs nvm 简介 nvm&#xff08;Node Version Manager&#xff09;是 node.js 的版本管理器&#xff0c;可以让我们轻松地在不同的 node.js 版本之间进行切换。 今天…

c语言(指针进阶)

指针 一.什么是字符指针二.使用指针数组模拟二维数组三.函数指针 一.什么是字符指针 字符指针&#xff1a;指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。即将字符串的首地址赋值给字符指针&#xff0c;可让字符指针…

vivado RAM HDL Coding Guidelines

从编码示例下载编码示例文件。 块RAM读/写同步模式 您可以配置块RAM资源&#xff0c;为提供以下同步模式给定的读/写端口&#xff1a; •先读取&#xff1a;在加载新内容之前先读取旧内容。 •先写&#xff1a;新内容立即可供阅读先写也是众所周知的如通读。 •无变化&…

树和堆的精讲

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

告别你的朝九晚五的工作。

告别你的朝九晚五的工作。 以下是6个网站&#xff0c;你可以从任何地方获得100美元到2000美元的报酬&#xff1a; &#xff08;第3个网站最正规&#xff09; 1. Honeygain 分享未使用的互联网带宽换取现金。 功能包括推荐系统、JumpTask模式、成就和每日幸运抽奖。 非常适…

机构如何搭建一个在线课程教学平台?

随着数字化教育的兴起&#xff0c;越来越多的教育机构开始考虑建立自己的在线课程教学平台。这一趋势不仅顺应了时代的发展&#xff0c;而且为教育行业带来了诸多便利和优势。构建一个在线教学平台可以帮助机构拓宽服务范围、提升教学质量、增强学生体验&#xff0c;并且能够有…

Qt之Qchar类的接口1

Qt类的构造函数 QChar类提供了许多个不同原型的构造函数&#xff0c;以方便不同场合下使用。 QChar(); //构造一个空字符&#xff0c;即‘\0’ QChar(char ch); //由字符数据ch构造 QChar(uchar ch)…

optuna,一个好用的Python机器学习自动化超参数优化库

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️付费专栏:Python专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前言 超参数优化是机器学习中的重要问题,它涉及在训练模型时选择最优的超参数组合,以提高模型的性能和泛化能力。Optuna是一个用于自动化超参数优化的…