专题四:设计模式总览

前面三篇我们通过从一些零散的例子,和简单应用来模糊的感受了下设计模式在编程中的智慧,从现在开始正式进入设计模式介绍,本篇将从设计模式的7大原则、设计模式的三大类型、与23种设计模式的进行总结,和描述具体意义。

设计模式体系结构图

七大原则

开闭原则(OCP,Open Close Principle )

官方解释:对扩展开放软件实体应当对修改关闭(Software entities should be open for extension,but closed for modification)


白话解读:合成复用原则、里氏替换原则相辅相成,都是开闭原则的具体实现规范
就是有新的业务的时候,尽可能扩展展新类而不是修改旧类,实际开发的时候大家基本上都会这么做,这样对之前的业务影响比较小,所以很多原则设计模式就贯穿在日常开发中。
 

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

👀官方解释:继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that anyproperty proved about supertypeobjects also holds for subtype objects)

是不是完全看不明白,俺也一样。

大白话:其实就是当我们需要开发功能的时候尽量通过继承父类,扩展父类功能的;而不是直接在原本的父类上修改。

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

官方定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该以来抽象(High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions )

大白话:面向接口编程,而不是面向类编程:

例如:Controller里注入的是service接口而不是ServiceImpl,这样Controller就不用关心servcieImpl到底依赖的那些dao,同理可以推广的ServiceImpl依赖的是dao接口而不用关心具体实现。

单一原则(SRP,Single Responsibility Principle)

官方定义:一个类应该有且仅有一个引起他变化的原因,否则这个类应该被拆分(There should never be more than one reason for a class to change)

大白话:每个类只负责自己领域类的事情,而不是一篮子事情:当然要注意不是一个类只有一个方法。

例如:FileService可以处理文件上传下载方法,但是注册登陆等方法就不该放入该类中

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

官方定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another on should depend on the smallest possible interface)

大白话:类应该建立自己专用的接口,而不是建立一个万能接口。

其实这个可以配合上面两个看,如果建立的万能接口,同时又要满足上面依赖倒置原则,那必定实现不了单一原则。

合成复用原则(CRP,Composite Reuse Principle)

由叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)

软件复用时,要尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系实现。

大白话:优先是用组合而不是继承,这个是通用原则了,主要还是继承扩展起来没那么方便。

迪米特法则(LOD,Law of Demeter)

低耦合,高内聚。

最少知识原则(Least Knowledge Principle,.LKP)
只与你的直接朋友交谈,不跟“陌生人”说话 (TaIk only to your immediate friends and not to strangers)


其实就是引入一个中间者来进行交互Controller层不直接和Dao层直接交互

设计模式与组件生命周期关系图

以Java为例,通常我们会以结构型设计模式去构建类的类信息,通过创建型设计模式去创建对象,通过行为模式去控制对象的行为。

23种设计模式概述

前面三篇文章,基本上都多少涉及到了一点,这里咱们做个简单归纳的归纳总结,后面咱们咱们会一个一个的拆分。

结构型设计模式

外观模式(Facade)

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

例子:在一个电商系统中,处理订单可能涉及多个子系统,如支付系统、库存系统、物流系统等。外观模式可以提供一个统一的接口来处理订单,从而简化客户端的调用。

适配器模式(Adapter)

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

例子:MyBatis中的ResultSetHandler将JDBC的结果集转换为自定义的对象类型。这个过程就是适配器模式的应用,将不兼容的接口进行适配。

桥接模式(Bridge)

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

例子:Spring的JdbcTemplate和各种具体的数据库实现之间的分离。JdbcTemplate提供抽象操作,而具体的数据库操作由不同的DataSource实现,二者可以独立变化。

组合模式(Composite)

将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。

例子:在一个文件系统中,文件和文件夹都可以视为节点。文件夹可以包含文件和其他文件夹,这形成了一个树形结构。组合模式使得文件和文件夹的操作一致。这个例子可能有过文件处理系统的小伙伴可能才好理解,再举一个列子:一个管理系统他的菜单树结构的,子与父结构相同这样是不是就好理解了一级菜单二级菜单嘛,无限套娃就行。

装饰模式(Decorator)

动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator模式比生成子类更为灵活。

例子:在一个图形编辑器中,可以对图形对象添加不同的装饰(如边框、阴影)。装饰模式可以动态地添加这些装饰,而不需要改变图形对象的类。

享元模式 (Flyweight)

运用共享技术有效地支持大量细粒度的对象。 例子:在一个文字处理系统中,每个字符都是一个对象。如果每个字符都单独存储,会占用大量内存。享元模式通过共享相同的字符对象,减少内存的使用。

代理模式(Proxy)

其他对象提供一种代理以控制对这个对象的访问。

例子:在一个远程服务调用系统中,客户端通过代理对象来访问远程服务。代理对象处理与远程服务的通信和数据传输,客户端只需调用代理对象的接口。SpringCloud种的Open Feign就是这个思想呢,当然代理我们一般分为静态代理和动态代理,但是现在很多时候静态代理和装饰器思想很接近,所以很多时候代理模式指的是动态代理,而java实现动态代理由离不开jdk或cglib,详细讨论的时候这一块将是重点。同样提到这里咱们就不得再提一下Spring种的Aop咯。

Spring的AOP也是代理模式的一个典型应用。通过创建代理对象,Spring AOP可以在方法调用前后添加额外的逻辑,如事务管理、权限检查等。

MyBatis也使用代理模式生成Mapper接口的实现类,调用实际的SQL操作。这个在框架中使用特别多,后面一定要好好说说。

创建型设计模式

创建模式主要的关注点是怎样将对象创建出来,将对象创建与使用进行剥离。像Spring框架就是依赖创建型设计模式,将我们开发者从对象的创建给剥离出来,对象的创建全部交给Spring托管,咱们安心写自己业务代码(方法的调用即可)。

单例模式(Singleton Pattern)

单例模式,在系统里,你要判断一下,如果有一些类,只需要一个实例对象就可以了,那就给那个类做成单例的模式(关键点就是构造方法私有化)Spring中的Bean对象默认都是单例的,后面在介绍具体的设计模式的时候在详细看代码。

简单工厂模式

工厂模式的核心思想,其实就是不要自己在代码里手动new一个实现类对象出来,因为那样的话,调用方就不是面向接口编程了,你还得自己去实现了。所以一般都是用工厂的思想来提供所有实现类实例,然后调用方面向接口来编程即可,接口不变,调用方代码不用变。

现在基本上很少去自己实现工厂模式了,因为有Spring框架,Spring的容器其实本质上就是个大工厂的概念,将你需要的对象都放到Spring容器里,需要的时候问Spirng取就完事。

工厂方法模式(Factory Method)

模板方法模式+简单工厂模式,简单工厂模式

抽象工厂模式(Abstract Factory)

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。在一个工厂中聚合多个同类产品的创建方法。简单理解为“工厂的工厂即可”。

建造者模式(Builder)

构造一个复杂的对象,很多的属性,有些属性构造的时候需要做一些校验,格式转换;

举个例子:当我们销售系统收到一笔订单的时候,在构建订单的时候需要对订单金额基础的校验、对产品的代码、产品类型做一些转换、供应商等。

原型模式(Prototype)

原型模式,以某一个对象为原型,然后对这个对象进行拷贝,得到拷贝后的另外一个对象

例子:很多BeanUtils之类的都有类似的功能,后面我们在分析具体的原理和对比他们的性能等。

行为型设计模式

责任链模式(Chain Of Responsibility)

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止。

在Spring框架中,过滤器链的实现就体现了责任链模式的应用。每个过滤器负责处理请求的一部分逻辑,然后将请求传递给链中的下一个过滤器。这样就形成了一条过滤器链,所有的过滤器按顺序依次执行。

策略模式(Strategy)

可以将一系列算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。

例子:在一个支付系统中,支持多种支付方式(如支付宝、微信、信用卡、PayPal、比特币等)。每种支付方式是一个策略,用户可以在支付时选择不同的支付方式。

模版方法(Template Method)

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

例子:在一个文档处理系统中,可以有不同类型的文档(如Word文档、PDF文档等)的导出。导出的步骤大致相同:打开文档、读取内容、写入目标格式、关闭文档。具体的读取和写入步骤可以由子类实现。

命令模式(Command)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

例子:在一个智能家居系统中,可以有各种命令(如小爱同学打开灯、关闭灯、调节温度等)。每个命令可以封装为一个对象,用户可以对命令进行操作,如执行、撤销等。

其实对于前后端分离项目,用户前端每次执行一个操作,对应后端的每个请求都可以看作是一个命令的执行,我们提供给前端的接口其实就是一个个封装的对象。

观察者模式(Observer)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

例子:在一个股票交易系统中,投资者可以订阅股票价格的变化。当股票价格发生变化时,系统会通知所有订阅了该股票的投资者。

这个在Spring中的监听机制,其实就是可以看作是一个观察者模式,当模式泛化以后可以将消息中间件中的主题中的一组生产者一组消费者看作观察者模式。

访问者(Visitor)

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

例子:在一个编译器中,可以有不同的操作(如语法检查、代码优化、代码生成等)作用于语法树的各个节点。每个操作可以通过访问者模式实现。

状态模式(State)

允许对象在内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

例子:在一个订单处理系统中,订单可以有不同的状态(如新建、已支付、已发货、已完成等)。每种状态下订单的行为可能不同,比如在已支付状态下可以发货,但在新建状态下不可以。

这个状态模式和策略模式很想,不同的是状态模式有一个自动流转的状态机通过状态的不同来实现不同的算法。

解释器模式(Interpreter)

给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

例子:在一个数学表达式计算器中,可以定义表达式的文法,并使用解释器模式来解析和计算表达式的值。

这玩意不知道咋解释,实际项目种使用的比较少,之前在科技部门做个一个通用的消息触达平台,定义了一些简单的占位符,通过占位符规则替换一些数据,应该类似解释器。

泛化到语言层面如Java 语言的Javac命令可以将Java源代码解析成Class文件,抑或现在比较火的Transformer框架中的编码器与解码器,将各种结构化数据解析成一组组向量后在来处理相似性问题,这个似乎更满足解释器模式的概念。这个模式先放一放,如果有小伙伴有比较深入的研究欢迎私信一起交流。

迭代器模式(Iteration)

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

例子:在一个集合框架中,可以有各种集合(如数组、列表、集合等)。迭代器模式提供了一个统一的接口来遍历这些集合中的元素。

这种Java已经有很成熟的API了,咱们就不用自己去实现了,设计模式一定不能为了设计而设计,一定是为了解决实际问题。

中介者模式(Mediator)

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

例子:在一个聊天系统中,用户之间的消息传递通过一个中介者对象(如聊天服务器)进行,用户不需要直接知道其他用户的存在。

备忘录模式(Memento)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复对象到先前的状态。

例子:在一个文本编辑器中,可以有撤销和恢复功能。编辑器可以在执行某个操作前保存当前状态,以便用户需要时可以恢复到该状态。

其实Java中的序列化与反序列化可以看作是这个思想的扩展,需要保留的时候序列化的文件中,

设计模式带来的一些思考:

1、为什么学习设计模式

其实回答这个问题的时候,很多培训机构的老师或者网上资料都说设计模式是源码的基础,只有学会了设计模式才能看懂源码之类的观点。

这里的观点可能不太一样,设计模式也好抑或其他框架也罢,都是为了我们解决实际问题而设计出来的,有了设计模式的思想很多时候会给我们软件设计带来一些启发,这种场景是否可以有xxx模式的思想来解决呢?

其次任何一个成熟的项目都不可能由一个人来完成,设计模式思想的运用可以在团队合作的时候来规范团队。举个例子:在参与基金销售系统开发的时候,他的交易可以各种业务如:基金的认购、申购、赎回、修改分红方式等,每一种业务都有对应的处理逻辑,但是他们都需要和基金管理公司交互而和基金公司交互都是通过文件的形式。这样他们共同点可以抽象出来,那就是文件的处理可以放到模板方法统一实现,文件解析后的数据,再有个各子域去实现。这样子域可以由不同的人员来开发,代码风格更加统一好维护。

总结

在软件开发中,设计模式不仅仅是技术上的工具,更是一种思维方式和解决问题的哲学。它们通过丰富的实践和理论积累,帮助开发者在面对复杂需求和变化时保持清晰的思路和高效的开发策略。无论是单例模式的实例唯一性,还是策略模式的算法灵活性,设计模式都在实际项目中展现出其独特的价值。通过不断学习和应用设计模式,我们能够更好地理解和运用面向对象设计的原则和技巧,从而打造出更加健壮和可维护的软件系统。设计模式的智慧不仅体现在代码中,更融入到整个软件开发的文化与实践中,成为提升团队能力和项目质量的重要支柱,同样切记不能为了设计而设计,目前各种语言与框架已经很成熟,使用这些设计模式的时候一定要采取审慎原则。拿不定主意的时候多和公司资深前辈沟通。

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

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

相关文章

Docker-compose单机容器集群编排

传统的容器管理:Dockerfile文件 -> 手动执行 docker build 一个个镜像的构建 -> 手动执行 docker run 一个个容器的创建和启动 容器编排管理:Dockerfile文件 -> 在docker-compose.yml配置模板文件里定义容器启动参数和依赖关系 -> 执行dock…

PlantUML-UML 绘图工具安装、Graphviz安装、本地使用/在线使用、语法、图示案例

文章目录 前言本地安装vscode安装插件下载安装Graphviz配置Graphviz环境变量测试 在线使用演示PlantUML语法总结活动图(新语法)时序图类图用例图其他图 更多相关内容可查看 前言 本篇提供两种使用方式分别为 在线使用地址1:https://www.pla…

在安卓手机上原生运行docker

前言 之前的文章(香橙派5plus上跑云手机方案一 redroid(带硬件加速))在Ubuntu的docker里运行安卓,这里说下怎么在安卓手机下运行docker,测试也可以跑Ubuntu。 想在手机上运行docker想的不是一天两天了,其实很久之前就有这个想法了&#xff…

Docker网络模式和Cgroup资源限制

目录 1、Docker网络 (1)Docker网络实现原理 查看容器的输出和日志信息 2、Docker 的网络模式 查看docker列表 (1)网络模式详解 1)host模式 2)container模式 3)none模式 4)br…

SpringCloud教程 | 第十篇: 读取Nacos的配置

1、nacos服务器选用 2、test.yaml这一个DataId配置如下: config:name: aabb222 spring:application:name: testdatasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/hmblogs?useUni…

MongoDB教程(十二):MongoDB数据库索引

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 引言一、MongoD…

【鸿蒙OS】【ArkUI】鸿蒙OS UI布局适配终极攻略

鸿蒙OS UI布局适配终极攻略 像素适配大法,此方法也适合Android ArkUI为开发者提供4种像素单位,框架采用vp为基准数据单位。 vp相当于Android里的dp fp相当于Android里的sp 官方是如何定义的呢,如下图 今天我来教大家如何用PX做到ArkUI的终级适配&…

Leetcode 2011. 执行操作后的变量值

问题描述: 存在一种仅支持 4 种操作和 1 个变量 X 的编程语言: X 和 X 使变量 X 的值 加 1--X 和 X-- 使变量 X 的值 减 1 最初,X 的值是 0 给你一个字符串数组 operations ,这是由操作组成的一个列表,返回执行所有…

C++初学者指南-5.标准库(第一部分)--标准库算法介绍

C初学者指南-5.标准库(第一部分)–标准库算法介绍 文章目录 C初学者指南-5.标准库(第一部分)--标准库算法介绍C的标准算法是:第一个示例组织输入范围自定义可调用参数并行执行(C17)迭代器和范围的类别错误消息命名空间std::ranges中的算法 (C20)算法参数图标相关内容…

linux 安装 RocketMQ 4.7

安装介绍 Centos 7RocketMQ 4.7JDK 1.8 (安装JDK参考)RocketMQ的官网地址: http://rocketmq.apache.orgGithub地址是 https://github.com/apach e/rocketmq 安装操作 下载RocketMQ RocketMQ运行版本下载地址: Rocketmq-all-4.7.1-bin-release.zip …

httpx,一个网络请求的 Python 新宠儿

大家好!我是爱摸鱼的小鸿,关注我,收看每期的编程干货。 一个简单的库,也许能够开启我们的智慧之门, 一个普通的方法,也许能在危急时刻挽救我们于水深火热, 一个新颖的思维方式,也许能…

AI大模型新纪元:哪四大趋势引领未来智能革命?

在人工智能热潮持续居高不下背景下,虽然全球AI大模型企业卷参数的激烈程度有所放缓,但大模型仍不断朝着万亿、十万亿参数发展,并推动多模态持续演进以通向AGI。同时,大模型也在朝向轻量化、高效化、垂直多元化发展,进而…

每日复盘-20240718

20240718 六日涨幅最大: ------1--------300713--------- 英可瑞 五日涨幅最大: ------1--------301016--------- 雷尔伟 四日涨幅最大: ------1--------301016--------- 雷尔伟 三日涨幅最大: ------1--------301016--------- 雷尔伟 二日涨幅最大: ------1--------300713----…

Linux LVM扩容方法

问题描述 VMware Centos环境,根分区为LVM,大小50G,现在需要对根分区扩容。我添加了一块500G的虚拟硬盘(/dev/sdb),如何把这500G扩容到根分区? LVM扩容方法 1. 对新磁盘分区 使用fdisk /dev/sdb命令,进…

SpringCloud02_consul概述、功能及下载、服务注册与发现、配置与刷新

文章目录 ①. Euraka为什么被废弃②. consul简介、如何下载③. consul功能及下载④. 服务注册与发现 - 8001改造⑤. 服务注册与发现 - 80改造⑥. 服务配置与刷新Refresh ①. Euraka为什么被废弃 ①. Eureka停更进维 ②. Eureka对初学者不友好,下图为自我保护机制 ③. 阿里巴巴…

linux下JDK的安装

前言: 安装部署java开发的代码都需要java环境,这里记录下linux下JDK的安装过程,仅供学习参考。 JDK的下载 下载地址:https://www.oracle.com/java/technologies/downloads 选择和操作系统匹配的版本进行下载 查看操作系统&…

HarmonyOS NEXT学习——@BuilderParam装饰器

初步理解,相当于VUE的插槽slot Builder function overBuilder() {}Component struct Child {label: string ChildBuilder customBuilder() {}Builder customChangeThisBuilder() {}BuilderParam customBuilderParam: () > void this.customBuilder; // 使用自定…

人工智能未来发展前景将会怎样?

当我们探讨人工智能未来的发展前景时,可以从多个角度来详细说明其可能的影响和趋势: 技术进步与应用扩展 1.深度学习与机器学习: 进一步优化和算法进展:深度学习已经取得了巨大成就,但仍面临挑战,如对小数…

浅说区间dp(下)

文章目录 环形区间dp例题[NOI1995] 石子合并题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 [NOIP2006 提高组] 能量项链题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 [NOIP2001 提高组] 数的划分题目描述输入格式输出格式样例 #1样例输…

你也许不知道,自己可能是一个「热人」

稍微标题党了一下。: ) 今天想跟大家分享的,是一种很少有人了解的人格特质。它非常普遍,许多人都或多或少有一些倾向,但却很少有人意识到它。 不妨看一看,你有没有下面这些特征: 有着极其旺盛的求知欲,对许…