尚硅谷学习笔记——Java设计模式(一)设计模式七大原则

一、介绍

        在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,提出的解决方案。我们希望我们的软件能够实现复用性、高稳定性、扩展性、维护性、代码重用性,所以出现了设计模式的概念。 

设计模式常用的七大原则:

1)单一职责原则

2)接口隔离原则

3)依赖倒转原则

4)里氏替换原则

5)开闭原则ocp

6)迪米特法则

7)合成复用原则

 设计模式的目的:

1)代码重用性(即:相同功能的代码,不用多次编写)

2)可读性(即:编程规范性,便于其他程序员的阅读和理解)

3)可扩展性(即:需要增加新的功能时,非常方便)

4)可靠性(即:增加新的功能后,对原来的功能没有影响)

5)使程序呈现高内聚、低耦合的特性

二、单一职责原则 

        对一个类来说,一个类应该只负责一项职责。

1)降低类的复杂度,一个类只负责一项职责;

2)提高类的可读性,可维护性;

3)降低变更引起的风险;

4)通常情况下,我们应当遵守单一职责原则,只有逻辑足够简答,才能在代码级违反单一职责原则;只有类中的方法数量足够少,才可以在方法级别保持单一职责原则。

 三、接口隔离原则

        客户端不应该依赖它不需要的接口。即一个类对另一个类的依赖应该建立在最小的接口上。

改进前:

改进后: 

四、依赖倒转原则

1)高层模块不应该依赖低层模块,二者都应该依赖其抽象;

2)抽象不应该依赖细节,细节应该依赖抽象;

3)依赖倒转(倒置)的中心思想是面相接口编程;

4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定得多。在java中,抽象就是接口或抽象类,细节就是具体的实现类;

5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展示细节的任务交给它们的实现类去完成。

 改进前:

//方式1:实现Person接收消息的功能
class Person {public void receive(Email email) {System.out.println(email.getInfo());}
}

问题:如果我们获取的对方改变,Person也要增加相应的接受方法
解决思路:引入一个抽象的接口IReceiver,表示接受者,这样Person类与接口IReceiver发生依赖。其他实现都属于接收的范畴,只需各自实现IReceiver接口就可以。这样就符合依赖倒转原则

 改进后:对接口进行依赖,稳定性提高

//方式2//定义接口
interface IReceiver {public String getInfo();
}class Email implements IReceiver {public String getInfo() {return "电子邮件信息:hello, world";}
}class Person {public void receive(IReceiver receiver) {System.out.println(receiver.getInfo());}
}public class DependencyReceiver {public static void main(String[] args) {//客户端无需改变Person person = new Person();person.receive(new Email());}
}

依赖传递的三种方式

依赖传递有三种方式,这里只写调用方法ChangHong changHong = new ChangHong();1)通过接口传递实现依赖
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.open(changHong);2)通过构造器进行依赖传递
OpenAndClose openAndClose = new OpenAndClose(changHong);
openAndClose.open();3)通过setter方法传递
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setTv(changHong);
openAndClose.open();

 

五、里氏替换原则

继承性说明

1)父类中凡是实现好的方法,实际上是某种规范和契约,虽然不强制所有子类必须遵守,但如果子类对这些已实现的方法进行任意修改,就会对整个继承体系造成破坏。

2)继承的弊端:使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性。如果一个类被其他类继承,当这个类需要修改时,必须考虑所有子类。且父类修改后,所有涉及到子类的功能都可能发生故障。

3)为了解决这一问题,我们提出里氏替换原则

里氏替换原则

1)如果每个类型为T1的对象O1,都有类型为T2的对象O2,使T1定义的所有程序P在所有对象O1被替换成O2时,程序P没有任何变化。那么T2是T1的子类型。也就是说,所有引用基类的地方必须能透明的使用其子类的对象

2)在使用继承时,子类尽量不要重写父类的方法。

        比如说,A类中有m1、m2、m3三个方法,B类继承A类,且重写了A类的所有方法,这样就无法做到透明化。

        对此,我们可以让B类和A类共同继承一个更加基础的base类,使AB类耦合性降低,将AB类的公共方法提高base类中。

 

 六、开闭原则

介绍:

1)开闭原则是编程中最基础、最重要的设计原则。

2)一个软件实体如类、模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。

3)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

4)编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。

改进前: 

//用于绘图的类
class GraphicEditor {//接受Shape对象,根据type绘制不同的图形public void drawShape(Shape s) {if(s.m_type == 1) drawRectangle(s);else if(s.m_type == 2) drawCircle(s);}public void drawRectangle(Shape s) {System.out.println("绘制矩形");}public void drawCircle(Shape s) {System.out.println("绘制圆形");}
}//Shape类,基类
class Shape {int m_type;
}class Rectangle extends Shape {Rectangle(){super.m_type=1;}
}class Circle extends Shape {Circle(){super.m_type=2;}
}public classOcp {public static void main(String[] args) {//存在的问题GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());}
}

存在的问题:

        违反了设计模式的OCP原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。

思路:把shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现。这样当我们有新图形时,只需要让新的图形类继承shape、并实现draw方法。使用方的代码就不需要修改

改进后:

//用于绘图的类
class GraphicEditor {//接受Shape对象,根据type绘制不同的图形public void drawShape(Shape s) {s.draw();}
}//Shape类,基类
abstract class Shape {int m_type;public abstract void draw();
}class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("绘制矩形");}
}class Circle extends Shape {@Overridepublic void draw() {System.out.println("绘制圆形");}
}public classOcp {public static void main(String[] args) {//存在的问题GraphicEditor graphicEditor = new GraphicEditor();graphicEditor.drawShape(new Rectangle());graphicEditor.drawShape(new Circle());}
}

 七、迪米特法则

基本介绍:

1)一个对象应该对其他对象保持最少的了解

2)类与类的关系越密切,耦合度越大

3)迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息。所以陌生的类最好不要以局部变量的形式出现在类的内部。

注意事项:

1)迪米特法则的核心是降低类之间的耦合。

2)由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。

八、合成复用原则

尽量使用合成、聚合的方式,而不是使用继承。

从 is a 变成 has a

 

九、总结

1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

2)针对接口编程,而不是针对实现编程。

3)为了交互对象之间的松耦合设计而努力

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

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

相关文章

【iOS】知乎日报总结

文章目录 前言首页网络请求轮播图上滑加载图片请求 文章详情页WKWebView的使用点赞、收藏持久化——FMDB的使用 其他问题沙盒问题单元格点击其他 总结 前言 在系统学习了OC语言和UI控件后,知乎日报是第一个比较大的项目,耗时一个多月时间,里面…

SpringBoot宠物领养平台:设计与实现

摘 要 如今社会上各行各业,都在用属于自己专用的软件来进行工作,互联网发展到这个时候,人们已经发现离不开了互联网。互联网的发展,离不开一些新的技术,而新技术的产生往往是为了解决现有问题而产生的。针对于宠物领养…

使用 Python 剪辑视频的播放速度

要使用 Python 调整视频的播放速度,可以利用 moviepy 库中的 fx(特效)模块来实现这一功能。通过 moviepy.editor 中的 VideoFileClip 类和 fx.speedx 函数,可以轻松地调整视频的播放速度。 安装 moviepy 首先,确保已…

Vue.js 中的事件监听与事件修饰符

在 Vue.js 中,事件监听是一种常见的交互方式,它允许我们在用户与界面交互时执行特定的代码。Vue.js 提供了一种简洁的语法来绑定事件监听器,并且支持事件修饰符来控制事件的行为。在本篇博客中,我们将详细介绍 Vue.js 中的事件监听…

贝叶斯统计:高斯分布均值μ的后验分布推导

使用贝叶斯统计方法 均值 ( μ \mu μ ) 的后验分布解析 在贝叶斯统计中,后验分布表示在观察到数据后,对参数的更新后的信念。本篇文章将结合高斯分布的假设,详细解析均值 ( μ \mu μ ) 的后验分布推导过程,并对 ( μ \mu μ…

网安瞭望台第6期 :XMLRPC npm 库被恶意篡改、API与SDK的区别

国内外要闻 XMLRPC npm 库被恶意篡改,窃取数据并部署加密货币挖矿程序 网络安全研究人员发现了一起在 npm 包注册表上活跃了一年多的软件供应链攻击。名为 0xengine/xmlrpc 的 npm 包最初是一个无害的库,基于 JavaScript,用于 Node.js 的 XML…

D82【python 接口自动化学习- python基础之pytest基础用法

day82 pytest初体验 学习日期:20241128 学习目标:pytest基础用法 -- pytest初体验 学习笔记: 文件命名规范 py测试文件必须以test_开头(或_test结尾)测试方法必须以test开头测试类必须以Test开头,并且…

双向链表

目录 链表的分类 概念 双向链表的实现 ① 结构 ② 初始化 ③ 打印 ④ 插入数据 ⑤ 删除数据 ⑥ 查找数据 ⑦ 在pos位置之前插入数据 ⑧ 删除pos位置的数据 ⑨ 销毁链表 总结 链表的分类 虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构&…

怎么样才算得上熟悉高并发编程?

提到并发编程很多人就会头疼了;首先就是一些基础概念:并发,并行,同步,异步,临界区,阻塞,非阻塞还有各种锁全都砸你脸上,随之而来的就是要保证程序运行时关键数据在多线程…

微知-arp如何删除所有表项?(arp -d; ip neighbor delete 192.168.0.100)

ar命令删掉所有表项 sudo arp -d使用ip命令 ip neighbor delete 192.168.0.100

使用Gradle编译前端的项目

使用Gradle编译前端的项目 前言项目结构根项目(parent-project)的 settings.gradle.kts后端项目(backend)的 build.gradle.kts前端项目(frontend)的 build.gradle.kts打包bootJar 前言 最近的项目都是使用…

PyCharm中Python项目打包并运行到服务器的简明指南

目录 一、准备工作 二、创建并设置Python项目 创建新项目 配置项目依赖 安装PyInstaller 三、打包项目 打包为可执行文件 另一种打包方式(使用setup.py) 四、配置服务器环境 五、上传可执行文件到服务器 六、在服务器上运行项目 配置SSH解释…

clickhouse 分片键的重要性

文章目录 背景反思为啥出现问题为啥默认的语义是local 背景 问题背景 详细内容可以看这个 反思为啥出现问题 为啥会出现链接里出现的问题,对于goal join 和 join 语义不一样的问题,那是因为分片键设计不合理的情况 如果表a和表b 都是user_id 作分片键…

S4 UPA of AA :新资产会计概览

通用并行会计(Universal Parallel Accounting)可以支持每个独立的分类账与其他模块集成,UPA主要是为了支持平行评估、多货币类型、财务合并、多准则财务报告的复杂业务需求 在ML层面UPA允许根据不同的分类账规则对物料进行评估,并…

ffmpeg 各版本号对应表格

想看看ffmpeg各个版本对应表, #! /bin/bashFF_PATH$1 CURRENTpwd RESULT"$CURRENT/test_version.txt"cd $FF_PATHif [ -f $RESULT ]; thenrm $RESULT fifor i in git branch -a | grep remotes/origin/release/ | grep -v HEAD | grep -v master; dogit…

数据结构之堆:原理与实现

1. 什么是堆? 堆(Heap)是一种特殊的完全二叉树,它的每个节点都遵循以下性质之一: 最大堆(Max-Heap):每个节点的值都大于等于其子节点的值,根节点是最大值。最小堆&…

DreamCamera2相机预览变形的处理

最近遇到一个问题,相机更换了摄像头后,发现人像角度顺时针旋转了90度,待人像角度正常后,发现 预览时图像有挤压变形,最终解决。在此记录 一人像角度的修改 先放示意图 设备预览人像角度如图1所示,顺时针旋…

Linux信号量的编程

一&#xff0c;用信号量来实现是父进程先进行&#xff0c;还是子进程先进性 信号量的没有P&#xff0c;V操作之前&#xff0c;我们不知道如何控制&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>…

GPT相关的学术库——收藏更新自用

GOT-OCR2.0 General OCR Theory: Towards OCR-2.0 via a Unified End-to-end Model https://github.com/Ucas-HaoranWei/GOT-OCR2.0/tree/main ChatPaper 工具名称工具作用是否在线&#xff1f;在线预览备注ChatPaper通过ChatGPT实现对论文进行总结&#xff0c;帮助科研人进…

Linux或者Docker中时区查询和修改(差8小时问题)

前因&#xff1a; 当我们在Linux或者Docker中部署程序时&#xff08;无论.Net或者Java或者等等&#xff09;获取系统时间时&#xff08;例如C# DateTime.Now&#xff09;&#xff0c;和北京时间差8小时。 解决&#xff1a; 一、版本1 先放几个Linux下常用命令&#xff1a; …