当前位置: 首页 > news >正文

设计模式的七大原则

1.单一职责原则

单一职责原则(Single responsibility principle),即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、A2。所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

2.接口隔离原则

​接口隔离原则(Interface Segregation Principle, ISP)是面向对象设计的五大原则之一,由Robert C. Martin在《Agile Software Development, Principles, Patterns, and Practices》一书中提出。这一原则强调了在设计软件时,应当尽量保持接口的小而专一,以避免客户端依赖它不需要的方法。

具体来说,接口隔离原则提倡以下几点:

  1. 模块化设计:每个接口应该专注于提供一组相关功能,而不是试图满足所有客户端的需要。这样可以确保接口的高内聚性,每个接口都只负责一个功能模块。

  2. 客户端定制接口:为不同的客户端或用途定义不同的接口,而不是强迫它们去依赖并实现那些它们实际上并不使用的接口方法。这样可以减少不必要的耦合,提高系统的灵活性和可维护性。

  3. 避免胖接口:胖接口是指包含了大量方法的接口,这往往意味着使用该接口的类需要实现很多它实际上并不关心的方法。ISP建议将这样的大接口拆分为更小、更具体的接口,使得每个类仅需实现它真正需要的方法。

遵循接口隔离原则的好处包括:

  • 提高代码的可读性和可维护性。
  • 减少代码间的耦合度,易于修改和扩展。
  • 提升系统的灵活性,便于单元测试。
  • 促进设计的清晰和职责的单一。

下面我将以Java语言为例,简单展示如何应用接口隔离原则。

假设我们有一个系统需要处理不同类型的动物,一开始可能会设计一个包含多种行为的胖接口

// 胖接口示例
interface Animal {void eat();void fly();void swim();
}

然后,有一个Duck类实现了这个接口:

class Duck implements Animal {@Overridepublic void eat() {System.out.println("Duck is eating.");}@Overridepublic void fly() {System.out.println("Duck is flying.");}@Overridepublic void swim() {System.out.println("Duck is swimming.");}
}

但问题在于,并非所有动物都能飞和游泳,比如鸟可能不能游泳,鱼不能飞。这导致了不必要的方法实现,如对于一个只能游泳的鱼来说,实现fly()方法是没有意义的。

按照接口隔离原则改进,我们可以将接口拆分为更具体的几个:

interface Eater {void eat();
}interface Flyer {void fly();
}interface Swimmer {void swim();
}

现在,每个类只实现它需要的接口: 

class Duck implements Eater, Flyer, Swimmer {// 实现相应的方法...
}class Fish implements Eater, Swimmer {// 实现相应的方法,无需实现fly()
}

这样,Fish类就不需要实现fly()方法,因为它不符合鱼的行为特征,这便是接口隔离原则的应用实例,它让我们的设计更加合理和灵活。 

3.依赖倒转原则

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。

面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

 4.里氏替换原则

在软件中,子类型(子类)必须能够替换其基类型(父类),并且在软件中使用基类型的地方,都能够使用子类型而不引发任何错误或导致程序的行为异常。这意味着子类应当能够保持父类的行为约定,不会破坏或违背父类的预期用途。

具体来说,里氏替换原则强调以下几个关键点:

  1. 行为一致性:子类应当能够替换父类,且不会影响到程序的正确性。即,使用父类的地方可以用子类替代,而不会引起任何错误或意外行为。

  2. 合约不变:子类应当遵守父类定义的接口契约(即方法的前置条件和后置条件不能比父类更严格)。例如,如果父类的一个方法承诺在某种输入下一定会返回一个正数,那么子类的相同方法也必须满足这一承诺。

  3. 增加功能但不改变原有行为:子类可以添加新的方法或属性,但不得去除或修改父类已有的方法(除非是通过重写使行为更为一般化,但仍需满足上述的合约不变原则)。

遵循里氏替换原则的好处包括:

  • 增强代码的稳定性:确保现有代码在新子类加入时仍能正常工作。
  • 提高代码的可预测性:开发者可以依赖基类的行为,而不用担心子类的具体实现细节。
  • 促进良好的设计:鼓励设计者在设计类的继承结构时更加谨慎,考虑接口和实现的一致性。

违反里氏替换原则可能导致的问题:

  • 子类的行为与父类不一致,可能在运行时引发错误。
  • 系统变得更加脆弱,难以维护和扩展,因为子类的修改可能会影响到依赖父类的其他部分。

基类 Shape.java

public abstract class Shape {public abstract double area();
}

子类 Rectangle.java

public class Rectangle extends Shape {private double width;private double height;public Rectangle(double width, double height) {this.width = width;this.height = height;}@Overridepublic double area() {return width * height;}
}

使用这些类的 Main.java

public class Main {public static void main(String[] args) {Shape shape = new Rectangle(10, 5); // 这里展示了里氏替换原则的应用System.out.println("Area: " + shape.area()); // 输出矩形的面积}
}

在这个Java示例中,Rectangle类继承了抽象类Shape并实现了area()方法。在Main类中,我们创建了一个Rectangle对象,并将其向上转型为Shape类型的引用。这证明了子类对象可以替换父类对象,同时程序逻辑保持正确,体现了里氏替换原则的精神。通过将Rectangle实例赋给Shape类型的变量,我们展示了子类对父类的透明替换能力,而无需修改使用Shape的现有代码。
父类可以不是抽象类吗?

父类不一定是抽象类。里氏替换原则并没有规定父类必须是抽象类。它可以是一个具体的类,只要子类遵循其规则即可。实际上,里氏替换原则适用于所有类型的继承关系,不论是抽象类还是具体类作为基类。

这里有一个非抽象父类的例子:

父类 Vehicle.java

public class Vehicle {public void start() {System.out.println("Vehicle started.");}
}

子类 Car.java

public class Car extends Vehicle {@Overridepublic void start() {System.out.println("Car started with ignition.");}
}

在这个例子中,Vehicle是一个具体类,它有一个start()方法。Car类继承了Vehicle类并重写了start()方法,提供了更具体的实现。只要Car类中的start()方法不违反Vehicle类中该方法的预期行为(比如,没有改变车辆启动的基本含义或引入不可接受的行为变更,即车开起来细化一下,可以是汽车,公交车,卡车开起来,但都比车这个范围更小更细化),这就遵循了里氏替换原则。即使Vehicle类不是抽象的,我们依然可以将Car实例安全地用在任何期待Vehicle类型的地方。

5.开闭原则 

开闭原则(Open Closed Principle)一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。也就是当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。用抽象构建框架,用实现扩展细节。

开始 Display直接修改成BarChart

  • Display:用于显示各种图表。
  • BarChart:一种特定类型的图表,表示条形图。

重构此设计以符合开闭原则。开闭原则要求软件实体(模块、类或方法)应该对扩展开放,对修改关闭。这意味着我们应该能够轻松添加新功能(如新的图表类型)而不必更改现有的代码。

重构后的类结构图:

  • Display:负责显示图表。
  • AbstractChart:一个新的抽象类,定义了显示图表的方法。
  • BarChart:继承自AbstractChart,表示条形图。
  • LineChart:同样继承自AbstractChart,表示折线图。
// 抽象类,定义了显示图表的方法
abstract class AbstractChart {abstract void display();
}// 条形图类
class BarChart extends AbstractChart {@Overridevoid display() {System.out.println("Displaying bar chart");}
}// 折线图类
class LineChart extends AbstractChart {@Overridevoid display() {System.out.println("Displaying line chart");}
}// 显示器类,负责显示图表
class Display {void display(AbstractChart chart) {chart.display();}

现在,如果需要添加新的图表类型(例如饼状图),只需创建一个新的类(如PieChart),让它继承自AbstractChart,并实现display()方法。然后,在Display类中,您不需要更改任何现有代码就可以处理新的图表类型。这就是开闭原则的一个简单应用。

6.迪米特法则

迪米特法则,也被称为最少知识原则(Least Knowledge Principle, LKP),是一种面向对象设计中的指导思想,它的核心理念非常直白:“不要和陌生人说话”。这句话听起来像是一句社交建议,但在编程世界里,它帮助我们构建低耦合、高内聚的软件系统。

想象一下,在一个公司里,员工们专注于自己的工作,他们通常只会直接和紧密相关的同事沟通,比如他们的直接上司、团队成员或是直接对接的其他部门同事。他们不会也不应该去干涉或者直接联系公司里每一个不认识的人。这样做的好处是,每个人的工作职责清晰,减少了不必要的干扰,提高了工作效率。

在编程中,迪米特法则应用这个思路,要求一个对象应该尽量减少与其他对象的直接交互,特别是避免和那些它不直接依赖的对象交流。具体来说,一个对象应该只和它的直接朋友通信:

  • 直接的朋友包括:对象本身、对象的成员变量(属性)、传递给对象的方法参数,以及由该方法创建或返回的对象。

遵循迪米特法则,我们可以这样做:

  1. 限制访问范围:尽量使用private或protected修饰符限制类成员的访问权限,只暴露必要的接口给外部。
  2. 减少依赖:通过接口而非具体类编程,减少类之间的直接耦合。
  3. 使用中介者:当需要跨多个层级访问数据时,可以引入一个中介对象来协调,避免高层级的对象直接操作低层级的对象。

7.合成复用原则

合成复用原则(Composite Reuse Principle)就是是尽量使用合成/聚合的方式,而不是使用继承。合成复用原则则更像是使用标准化的建筑模块来组装房屋。每个模块,比如卧室模块、厨房模块、浴室模块,都是独立设计和制造的。当你需要建造一座新房子时,不是从零开始或者修改旧设计,而是选择需要的模块进行组合。这样,即使未来想升级卧室设计或者替换浴室设施,只需替换相应的模块即可,不影响整体房屋结构的稳定性和其他部分的正常使用。

放到编程领域,这意味着我们应当尽量使用对象组合(即将对象作为其他对象的属性或通过集合管理)来实现新功能,而不是通过继承来扩展类。这样做能够使得系统更加灵活,易于维护和扩展,因为修改或添加功能时,不必触及或改动现有的类结构,只需调整或增加新的组件(对象)即可,保证了各个部分的独立性和复用性。就像是高效又灵活的建筑工地,随时可以根据需求调整设计方案,而不会造成大规模的返工。

相关文章:

设计模式的七大原则

1.单一职责原则 单一职责原则(Single responsibility principle),即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、…...

ThreeJS-3D教学十五:ShaderMaterial(noise、random)

ThreeJS-3D教学十四:ShaderMaterial(length、fract、step) 上面这篇主要是操作 fragmentShader 片元着色器,实现对物体颜色的修改,这次咱们来看下修改 vertexShader 顶点着色器,这个其实就是位移各个顶点的位置。 接下来我们先介绍下 noise 噪声函数(Perlin Noise、Sim…...

LeetCode 2974.最小数字游戏:排序+交换奇偶位

【LetMeFly】2974.最小数字游戏:排序交换奇偶位 力扣题目链接:https://leetcode.cn/problems/minimum-number-game/ 你有一个下标从 0 开始、长度为 偶数 的整数数组 nums ,同时还有一个空数组 arr 。Alice 和 Bob 决定玩一个游戏&#xff…...

使用vllIm部署大语言模型

使用vllm部署大语言模型一般需要以下步骤: 一、准备工作 1. 系统要求 - 操作系统:常见的 Linux 发行版(如 Ubuntu、CentOS)或 Windows(通过 WSL)。 - GPU 支持:NVIDIA GPU 并安装了适当的驱动程…...

静态搜索iOS动态链接函数的调用位置

静态搜索iOS动态链接函数的调用位置 可执行文件格式mach-O,是在苹果的操作系统 macOS 和 iOS 上使用的一种二进制文件格式。 在一些iOS安全扫描中,可能存在需要获取函数具体调用位置的需求,能指导用户更精确的定位漏洞。 现在以NSLog函数为例&#xff…...

【鸿蒙学习笔记】尺寸设置・layoutWeight・对子组件进行重新布局

官方文档:尺寸设置 目录标题 layoutWeight:对子组件进行重新布局 layoutWeight:对子组件进行重新布局 设置了layoutWeight属性的子元素与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设置。 // 引入包名 import { http…...

vue实现表单输入框数字类型校验功能

vue实现表单输入框数字类型校验功能 1. 样式代码 <el-form-item label"订单总价"><el-input size"small" v-model"form.totalPrice" placeholder"请输入订单总价 正整数或者2位数小数" input"check(form.totalPric…...

JS登录页源码 —— 可一键复制抱走

前期回顾 https://blog.csdn.net/m0_57904695/article/details/139838176?spm1001.2014.3001.5501https://blog.csdn.net/m0_57904695/article/details/139838176?spm1001.2014.3001.5501 登录页预览效果 <!DOCTYPE html> <html lang"en"><head…...

Kithara与OpenCV (一)

Kithara使用 OpenCV 库 目录 Kithara使用 OpenCV 库简介需求和支持的环境构建 OpenCV 库使用 CMake 进行配置以与 Kithara 一起工作 使用 OpenCV 库设置项目运行 OpenCV 代码图像采集和 OpenCV自动并行化限制和局限性1.系统建议2.实时限制3.不支持的功能和缺失的功能4.显示 Ope…...

什么是软件定义安全SDSec

一、软件定义安全SDSec产生的背景 软件定义安全&#xff08;Software Defined Security&#xff0c;SDSec&#xff09;的产生背景主要源于传统网络安全防护方法在面对复杂网络环境时的不适应性&#xff0c;以及软件定义网络&#xff08;SDN&#xff09;技术的发展和应用。 SD…...

【C语言】C语言可以做什么?

目录 1. 操作系统开发1.1 操作系统内核1.2 设备驱动程序1.3 系统工具和实用程序 2. 嵌入式系统2.1 微控制器编程2.2 传感器和执行器控制2.3 消费电子产品 3. 应用程序开发3.1 图形用户界面应用3.2 游戏开发3.3 多媒体处理 4. 网络编程4.1 网络协议实现4.2 服务器和客户端程序4.…...

WordPress 主题技巧:给文章页增加“谁来过”模块。

模块功能&#xff1a; 我个人目前在做一个电影类的网站&#xff0c;在开发文章页的模版时候&#xff0c;突然觉得给文章页增加一个“谁对本电影感兴趣”的功能模块可能会比较有趣&#xff0c;这个功能有点类似于‘足迹’的感觉&#xff0c;用户可以通过这个功能&#xff0c;发…...

【vue组件库搭建07】Vitest单元测试

vitest官网 vue-test-utils 我们的测试框架选择的是 Vitest 和 vue-test-utils。两者的关系为&#xff1a; Vitest 提供测试方法&#xff1a;断言、Mock 、SpyOn 等方法。vue-test-utils: 挂载和渲染组件&#xff1a; Vue Test Utils 允许您在隔离中挂载组件&#xff0c;这意…...

JSONObject和Map<String, Object>的转换

一、前言 Java开发中出参返回和入参传入更灵活的方法是使用Map<String, Object>入参或出参&#xff0c;或者使用JSONObject。 1、好处&#xff0c;参数可变&#xff0c;对接口扩展性很友好。 public ResponseData<WXModelDTO> getUserInfo(RequestBody Map<…...

C# 建造者模式(Builder Pattern)

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;也被称为生成器模式&#xff0c;是一种对象构建模式&#xff0c;旨在将复杂对象的构建过程与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。这种模式特别适用于构建具有多个组成部分的复杂对象&am…...

初阶数据结构速成

本篇文章算是对初阶数据结构的总结&#xff0c;内容较多&#xff0c;请耐心观看 基础概念部分 顺序表 线性表&#xff08; linear list &#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使 ⽤的数据结构&#xff0c;常⻅的线性表&#xff1a;…...

nx上darknet的使用-目标检测-在python中的使用

1 内置的代码 在darknet中已经内置了两个py文件 darknet_video.py与darknet_images.py用法类似&#xff0c;都是改一改给的参数就行了&#xff0c;我们说一下几个关键的参数 input 要预测哪张图像weights 要使用哪个权重config_file 要使用哪个cfg文件data_file 要使用哪个da…...

Python高级(四)_内存管理

Python高级-内存管理 第四章 内存管理 1、对象池 小整数池 系统默认创建好的,等着你使用 概述:整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。Python 对小整数的定义是 [-5, 256] ,这些整数对象是提前建立好的…...

关键路径-matlab

路径上边的数目称为路径长度 图的基本知识 求最短路径&#xff08;Dijkstra算法&#xff09; 2. 待继续尝试 ①Dijkstra ②floyd_all.m 一 二 ③ LeetCode [329. 矩阵中的最长递增路径]...

JavaDS —— 单链表 与 LinkedList

顺序表和链表区别 ArrayList &#xff1a; 底层使用连续的空间&#xff0c;可以随机访问某下标的元素&#xff0c;时间复杂度为O&#xff08;1&#xff09; 但是在插入和删除操作的时候&#xff0c;需要将该位置的后序元素整体往前或者向后移动&#xff0c;时间复杂度为O&…...

LangChain —— Message —— how to filter messages

文章目录 一、概述二、基本使用三、连成链 一、概述 在更复杂的链和代理中&#xff0c;我们可能会使用消息列表跟踪状态。此列表可以开始累积来自多个不同模型、说话者、子链等的消息&#xff0c;我们可能只想将此完整消息列表的子集传递给链/代理中的每个模型调用。 filter_me…...

conda install问题记录

最近想用代码处理sar数据&#xff0c;解放双手。 看重了isce这个处理平台&#xff0c;在安装包的时候遇到了一些问题。 这一步持续了非常久&#xff0c;然后我就果断ctrlc了 后面再次进行尝试&#xff0c;出现一大串报错&#xff0c;不知道是不是依赖项的问题 后面看到说mam…...

【python】IPython的使用技巧

IPython使用技巧 一、魔法命令 %timeit 用途&#xff1a;用于测量一段代码的执行时间&#xff0c;这对于评估代码的性能非常有帮助&#xff0c;尤其适用于需要进行性能优化和比较不同实现方式效率的场景。示例&#xff1a;%timeit [x**2 for x in range(1000)]扩展&#xf…...

常用知识点问答

kafka如何部署&#xff1f; 先说明kafka的版本如果是 2.X 版本&#xff0c;则要先部署 3或5 个服务器的zookeeper集群&#xff0c;然后在每个zookeeper服务器上部署kafka应用。如果是 3.X 版本&#xff0c;kafka不再依赖zookeeper&#xff0c;所以可以直接在java17的环境上部署…...

paddlenlp cpu windows 下测试gpt

paddlenlp 安装python3.11版本 conda create -n python311 python3.11 激活python conda activate python311 安装paddlepaddle conda install paddlepaddle3.0.0b0 -c paddle pip install paddlenlp3.0.0b0 -U -i https://pypi.tuna.tsinghua.edu.cn/simple windows下…...

uboot的功能

uboot裸机程序&#xff0c;uboot的核心功能是启动内核 uboot启动流程 XIP设备&#xff1a; 1、硬件初始化 2、读flash上面的内核&#xff0c;拷贝进内存 3、启动内核 非XIP设备 1、BROM程序拷贝uboot到RAM 2、执行uboot 3、硬件初始化 4、读flash上面的内核&#xff0c;拷贝进…...

java导出word实现

参考&#xff1a;Poi-tl Documentation...

Flink 提交作业的方式

首先我进行了flink单机部署&#xff0c;个人建议不管是学习还是开发尽量不使用 然后开始了flink自带集群部署&#xff0c;部署在三台服务器上&#xff0c;资源管理由flink集群自己管理&#xff0c;然后为了解决集群的单点故障问题&#xff0c;使用zookeeper监听事件&#xff0…...

JVM系列 | 垃圾收集算法

JVM系列 | 垃圾收集算法 文章目录 前言如何判断对象已"死"&#xff1f;引用计数法可达性分析算法可达性分析2.0版 | 引用的增强对象的消亡过程回收方法区主要回收目标&#xff1a;回收操作 垃圾收集算法分代收集理论 与 跨代引用假说分代收集理论跨带引用假说 垃圾收…...

深入理解Spring Boot中的事件驱动架构

深入理解Spring Boot中的事件驱动架构 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 1. 引言 事件驱动架构在现代软件开发中越来越受欢迎&#xff0c;它能够提高系统的松耦合性和可扩展性。Sprin…...

网站开发课程设计/推广引流软件

GridView嵌套GridView,对子GridView及父GridView级联删除的问题 http://blog.csdn.net/amandag/archive/2007/05/31/1633395.aspx 利用GridView显示主细表并添加删除、打开、关闭功能&#xff08;续&#xff09; http://dotnet.aspx.cc/article/f73eeaa9-2bdc-47fd-afd2-59f2fa…...

app开发公司杭州/网络舆情优化公司

经常有人问我有关“大数据”的问题&#xff0c;而且多半情况下我们似乎是在各种不同的抽象和理解级别进行交谈。实时 和高级分析 之类的词语频频现身&#xff0c;并且我们总是立即开始谈论产品&#xff0c;这通常并不是一个好主意。 希望将类似本文的技术文章发送到您的收件箱吗…...

如何创建本地站点/推广优化厂商联系方式

下面是一个简单测试&#xff0c;虽然不够准确但是简单立即可行&#xff0c; 当前目录的IO写读测试&#xff1a; &#xff08;写&#xff09; dd if/dev/zero oftest bs64k count16k convfdatasync &#xff08;读&#xff09; dd of/dev/null iftest bs64k count16k convfdat…...

asp.net网站怎么做/网页制作工具

一、Docker架构 Docker 包括三个基本概念: 镜像&#xff08;Image&#xff09;&#xff1a;Docker 镜像&#xff08;Image&#xff09;&#xff0c;就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。 容器…...

淄博企业网站建设有限公司/seo托管服务

一、关于Nginx的负载均衡 在服务器集群中&#xff0c;Nginx起到一个代理服务器的角色&#xff08;即反向代理&#xff09;&#xff0c;为了避免单独一个服务器压力过大&#xff0c;将来自用户的请求转发给不同的服务器。 二、Nginx负载均衡策略 负载均衡用于从“upstream”模…...

腾博会的网站是什么/百度关键词seo推广

基础知识 jwt是由三部分构成的&#xff0c;第一部分是头部&#xff08;header&#xff09;&#xff0c;第二部分是载荷&#xff08;payload&#xff09;&#xff0c;第三部分为签证&#xff08;signature&#xff09; 头部 头部声明了类型和加密方法&#xff0c;如下 {typ:…...