软件开发:面向对象设计的七大原则!

news2024/12/24 8:42:18

七大原则

开闭原则、里氏代换原则、迪米特原则(最少知道原则)、单一职责原则、接口分隔原则、依赖倒置原则、组合/聚合复用原则。

开闭原则(The Open-Closed Principle ,OCP)

开闭原则:软件实体(模块,类,方法等)应该对扩展开放,对修改关闭。

概念理解

开闭原则是指在进行面向对象设计中,设计类或其他程序单位时,应该遵循:

(1)对扩展开放(open)

(2)对修改关闭(closed) 的设计原则。

开闭原则是判断面向对象设计是否正确的最基本的原理之一。

根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。

扩展开放:某模块的功能是可扩展的,则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。

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

开闭原则的实现方法

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

(1)可以把这些不变的部分加以抽象成不变的接口,这些不变的接口可以应对未来的扩展;

(2)接口的最小功能设计原则。根据这个原则,原有的接口要么可以应对未来的扩展;不足的部分可以通过定义新的接口来实现;

(3)模块之间的调用通过抽象接口进行,这样即使实现层发生变化,也无需修改调用方的代码。

接口可以被复用,但接口的实现却不一定能被复用。

接口是稳定的,关闭的,但接口的实现是可变的,开放的。

可以通过对接口的不同实现以及类的继承行为等为系统增加新的或改变系统原来的功能,实现软件系统的柔性扩展。

好处:提高系统的可复用性和可维护性。

简单地说,软件系统是否有良好的接口(抽象)设计是判断软件系统是否满足开闭原则的一种重要的判断基准。现在多把开闭原则等同于面向接口的软件设计。

开闭原则的相对性

软件系统的构建是一个需要不断重构的过程,在这个过程中,模块的功能抽象,模块与模块间的关系,都不会从一开始就非常清晰明了,所以构建100%满足开闭原则的软件系统是相当困难的,这就是开闭原则的相对性。

但在设计过程中,通过对模块功能的抽象(接口定义),模块之间的关系的抽象(通过接口调用),抽象与实现的分离(面向接口的程序设计)等,可以尽量接近满足开闭原则。

里氏替换原则(Liskov Substitution Principle ,LSP)

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

(1)不应该在代码中出现if/else之类对派生类类型进行判断的条件。

(2)派生类应当可以替换基类并出现在基类能够出现的任何地方,或者说如果我们把代码中使用基类的地方用它的派生类所代替,代码还能正常工作。

里氏替换原则(LSP)是使代码符合开闭原则的一个重要保证。

LSP体现

类的继承原则:如果一个派生类的对象可能会在基类出现的地方出现运行错误,则该派生类不应该从该基类继承,或者说,应该重新设计它们之间的关系。

动作正确性保证:从另一个侧面上保证了符合LSP设计原则的类的扩展不会给已有的系统引入新的错误。

里式替换原则的引申意义

子类可以扩展父类的功能,但不能改变父类原有的功能。

子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

子类中可以增加自己特有的方法。

当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。

当子类的方法实现父类的方法时(重载/重写或实现抽象方法)的后置条件(即方法的输出/返回值)要比父类更严格或相等。

里式替换原则的优点

约束继承泛滥,是开闭原则的一种体现。

加强程序的健壮性,同时变更时也可以做到非常好地提高程序的维护性、扩展性。降低需求变更时引入的风险。

迪米特原则(最少知道原则)(Law of Demeter ,LoD)

概念理解

对于面向OOD来说,又被解释为下面两种方式:

1)一个软件实体应当尽可能少地与其他实体发生相互作用。

2)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

朋友圈的确定

“朋友”条件:

1.当前对象本身(this)

2.以参量形式传入到当前对象方法中的对象

3.当前对象的实例变量直接引用的对象

4.当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友

5.当前对象所创建的对象

任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。

迪米特原则的优缺点

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

迪米特原则不希望类直接建立直接的接触。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特原则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系,这在一定程度上增加了系统的复杂度。

单一职责原则

永远不要让一个类存在多个改变的理由。

换句话说,如果一个类需要改变,改变它的理由永远只有一个。如果存在多个改变它的理由,就需要重新设计该类。

单一职责原则原则的核心含意是:只能让一个类/接口/方法有且仅有一个职责。

为什么一个类不能有多于一个以上的职责?

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

一方面,如果一个职责使用了外部类库,则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。

另一方面,某个用户由于某个原因需要修改其中一个职责,另外一个职责的用户也将受到影响,他将不得不重新编译和配置。

这违反了设计的开闭原则,也不是我们所期望的。

接口分隔原则(Interface Segregation Principle ,ISP)

不能强迫用户去依赖那些他们不使用的接口。

概念理解

换句话说,使用多个专门的接口比使用单一的总接口总要好。

它包含了2层意思:

接口的设计原则:接口的设计应该遵循最小接口原则,不要把用户不使用的方法塞进同一个接口里。如果一个接口的方法没有被使用到,则说明该接口过胖,应该将其分割成几个功能专一的接口。

接口的依赖(继承)原则:如果一个接口a继承另一个接口b,则接口a相当于继承了接口b的方法,那么继承了接口b后的接口a也应该遵循上述原则:不应该包含用户不使用的方法。 反之,则说明接口a被b给污染了,应该重新设计它们的关系。

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

指导意义

一个类对一个类的依赖应该建立在最小的接口上

建立单一接口,不要建立庞大臃肿的接口

尽量细化接口,接口中的方法尽量少

依赖倒置原则(Dependency Inversion Principle ,DIP)

高层模块不应该依赖于低层模块,二者都应该依赖于抽象

抽象不应该依赖于细节,细节应该依赖于抽象 C.针对接口编程,不要针对实现编程。

概念理解

依赖:在程序设计中,如果一个模块a使用/调用了另一个模块b,我们称模块a依赖模块b。

高层模块与低层模块:往往在一个应用程序中,我们有一些低层次的类,这些类实现了一些基本的或初级的操作,我们称之为低层模块;另外有一些高层次的类,这些类封装了某些复杂的逻辑,并且依赖于低层次的类,这些类我们称之为高层模块。

依赖倒置(Dependency Inversion)

面向对象程序设计相对于面向过程(结构化)程序设计而言,依赖关系被倒置了。因为传统的结构化程序设计中,高层模块总是依赖于低层模块。

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

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

类与类之间都通过抽象接口层来建立关系。

组合/聚合复用原则(Composite/Aggregate Reuse Principle ,CARP)

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

概念理解

即在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新对象通过向这些对象的委派达到复用已有功能的目的。就是说要尽量的使用合成和聚合,而不是继承关系达到复用的目的。

组合和聚合都是关联的特殊种类。

聚合表示整体和部分的关系,表示“拥有”。组合则是一种更强的“拥有”,部分和整体的生命周期一样。

组合的新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个组合关系的成分对象是不能与另一个组合关系共享的。

组合是值的聚合(Aggregation by Value),而一般说的聚合是引用的聚合(Aggregation by Reference)。

在面向对象设计中,有两种基本的办法可以实现复用:第一种是通过组合/聚合,第二种就是通过继承。

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

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

相关文章

隐马尔科夫模型

隐马尔可夫模型 隐马尔可夫模型(Hidden Markov Model, HMM)可以由观测序列推断出概率最大的状态序列. HMM中的变量可分为两组: 状态变量(隐变量): { y 1 , y 2 , . . . , y n } \left \{ y_{1},y_{2},...,y_{n}\right \} {y1​,y2​,...,yn​};序列标注问题中的标注. 观察变量…

《Python程序设计与算法基础教程(第二版)》江红 余青松 课后选择题 课后填空题答案

目录 第一章一、选择题二、填空题 第二章一、选择题二、填空题 第三章一、选择题二、填空题 第四章一、选择题二、填空题 第五章一、选择题二、填空题 第八章一、选择题二、填空题 第一章 一、选择题 Python语言属于 C A.机器语言 B.汇编语言 C.高级语言 D.以上都不是 在下列…

【python】数据预处理:分位数归一化 Quantile Normalization + INSCODE AI创作助手测试

文章目录 写在前面标准化/归一化python模块qnorm实现分位数归一化R代码实现分位数归一化分位数归一化 - NSCODE AI创作助手的回答*Q1:Quantile Normalization是什么?**Q2-1: 什么时候用Quantile normalization?**Q2-2: 什么时候做Quantile no…

VScode软件下载与中文设置

目录 一、下载软件 二、中文设置 一、下载软件 1.网站上下载 2.百度网盘链接下载 链接:https://pan.baidu.com/s/1PMhxaExwhurDVVPkkbbYmw?pwd507o 提取码:507o 就选择路径->同意许可->完成下载 二、中文设置 1.在左侧扩展图标中打开“EXT…

Linux - 第20节 - 网络基础(网络层)

1.IP协议 • IP协议全称为“网际互连协议(Internet Protocol)”,IP协议是TCP/IP体系中的网络层协议。 • 在主机通信的过程中,上层应用程序解决从应用层代码中获取数据并处理数据的问题,应用层解决的是读取完整报文、序…

Lion:Adversarial Distillation of Closed-Source Large Language Model

Lion:Adversarial Distillation of Closed-Source Large Language Model IntroductionMethodologyexperiment Introduction 作者表明ChatGPT、GPT4在各行各业达到很好的效果,但是它们的模型与数据都是闭源的。现在的主流的方案是通过一个老师模型把知识蒸馏到学生模…

明明开发薪资高,是这几点让我依旧选了测试...

不管是对刚毕业的大学生、工作几年的打工仔亦或者是久不入职场的老人来说,进入职场的方向都值得我们深思。 今天我就来解答下大家最常问的问题:开发和测试作为一个项目中很重要的角色,他们有什么区别呢? Python自动化测试&#x…

Benewake(北醒) 快速实现TFmini-S-IIC与电脑通信的操作说明

目录 1. 概述2. 测试准备2.1 工具准备2.2通讯协议转换 3. IIC通讯测试3.1 引脚说明3.2 测试步骤3.2.1 TFmini-S-IIC 与 PC 建立连接3.2.2 获取测距值3.2.3 更改 slave 地址 1. 概述 通过本文档的概述,能够让初次使用测试者快速了解测试 IIC 通信协议需要的工具以及…

Svn安装

目录 一. 软件环境 二. SVN服务端 1. yum安装svn 2. 查看安装的文件列表 3. 建立版本库 3.1 修改数据存储默认位置 3.2 使用svnadmin建立版本库 4. 配制 4.1 添加用户 4.2 配制读写权限 4.3 配制服务 5. 启动服务 5.1 停止服务 5.2 启动服务 5.3 拉取项目 三.…

Vivado下组合逻辑模块的仿真

文章目录 与门或门非门异或门同或门比较器半加器全加器乘法器数据选择器3-8 译码器三态门 组合逻辑电路的特点是任意时刻的输出仅仅取决于输入信号,输入信号变化,输出立即变化,其变化不依赖于时钟。 本文中的例子中模块名都是gate&#xff0c…

HSE健康安全环境管理,已成现代企业必备的一种管理工具

什么是HSE健康安全环境管理 HSE是英文单词Health,Safety,Environment的缩写,中文翻译就是健康、安全、环境管理的意思。HSE管理是一种科学、系统的企业管理方式,目的是为了以人为本,保障员工和环境的健康和安全&#…

express的使用(三) multer处理表单提交

个人博客 欢迎关注公众号:express的使用(三) multer处理表单提交 看前提示 本篇的主要流程是使用在前端提交一个文件,在nodejs编写的后端使用multer以及express进行接收,包括遇到的版本问题,如果是想要学习用body-parser、formidable、mul…

node.js版本与node-sass版本不一致解决

版本比较图 查看版本对比图: https://www.npmjs.com/package/node-sass node.js版本与node-sass版本不一致时npm install是可能会报错的。 安装对应版本 查看nodejs版本 CMD查看nodejs版本 node -v卸载不一致的node-sass npm uninstall node-sass安装指定版…

JAVA并发编程之锁应用

Java并发包是Java中提供的一个用于支持多线程编程的工具包。Java并发包提供了多种机制来控制线程的执行,保证线程的安全性和可靠性。下面我们将介绍Java并发包的使用方法,并给出示例。 synchronized public class SynchronizedDemo { ​private int v;…

华为云认证有什么?考试难不难?

最近几年华为云的市场占比越来越大,逐渐占据了我们生活中的方方面面,而且很多政企单位,也选择华为云作为合作伙伴,因此市场上也需要越来越多的华为云人才,早在几年前,华为云就已经推出了自己的人才认证系统…

条件随机场模型

条件随机场模型(Conditional Random Fields, CRF) 条件随机场是给定一组输入随机变量条件下,另一组输出随机变量的条件概率分布模型,其特点是假设输出随机变量构成马尔可夫随机场。线性链条件随机场,是输入序列对输出…

用 JavaScript 对抗 DDOS 攻击

继续趣事分享。 上回聊到了大学里用一根网线发起攻击,今天接着往后讲。 不过这次讲的正好相反 —— 不是攻击,而是防御。一个奇葩防火墙的开发经历。 第二学期大家都带了电脑,于是可以用更高端的方法断网了。但设备先进反而没有了 GEEK 的…

第十七章行为性模式—状态模式

文章目录 状态模式解决的问题反例 结构实例存在的问题使用场景 状态模式与策略模式的区别 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,它涉及算法与对象间职责的分配。行为型…

Java中常见转换-数组与list互转、驼峰下划线互转、Map转Map、List转Map、进制转换的多种方式

场景 Java中数组与List互转的几种方式 数组转List 1、最简单的方式,Arrays.asList(array); 创建的是不可变列表&#xff0c;不能删除和新增元素 String[] array new String[]{"a","b"};List<String> stringList Arrays.asList(array);System.ou…

嵌入式和单片机

凡是从事信息技术相关工作的&#xff0c;一定都听说过嵌入式和单片机。 大家都知道&#xff0c;这两个名词&#xff0c;和硬件系统有着非常密切的关系。 但是&#xff0c;如果要问具体什么是嵌入式&#xff0c;什么是单片机&#xff0c;它们之间究竟有什么区别&#xff0c;我…