【设计模式】23种设计模式之行为型模式

【设计模式】23种设计模式之行为型模式


gitee源代码:https://gitee.com/zhao_liangliang1997/untitled2023/tree/master/src

一、模板方法模式

1、豆浆制作问题

编写制作豆浆的程序,说明如下:
1)、制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎
2)、通过添加不同的配料,可以制作出不同口味的豆浆
3)、选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
4)、请使用 `模板方法模式` 完成(说明: 因为模板方法模式,比较简单,**很容易就想到这个方案**,因此就直接使用,不再使用传统的方案来引出模板方法模式 )

2、模板方法介绍

1)、模板方法模式,又叫模板模式,在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
2)、简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

3、原理及代码实现

原理:(1)SoyaMilk抽象类,类中实现了模板方法、定义了基本骨架方法;(2)PeanutSoyaMilk和RedBeanSoyaMilk继承并重写方法。

在这里插入图片描述

4、模板方法模式的钩子方法

1)、在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为 “钩子”
2)、还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配0料,请使用钩子方法对前面的模板方法进行改造;
3) 、代码演示:

在这里插入图片描述

5、模板方法模式的注意事项和细节

1)、基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改;
2)、实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用;
3)、既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现;
4)、该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大;
5)、一般模板方法都加上final关键字,防止子类重写模板方法模板方法;
6)、模式使用场景: 当要完成在某个过程该过程要执行一系列步骤这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理;

二、命令模式【难】

1、定义

1)、命令模式 (Command Pattern) : 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2)、命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3)、在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作
4)、通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军 (命令发布者) 、士兵(命令的具体执行者) 、命令(连接将军和士兵)

lnvoker是调用者 (将军) ,Receiver是被调用者 (上兵)MyCommand是命令,实现了Command接口,持有接收对象

2、原理类图及代码实现

在这里插入图片描述

对原理类图的说明-即(命名模式的角色及职责)

1)、Invoker 是调用者角色
2)、Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
3)、Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
4)、ConcreteCommand:将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute.

在这里插入图片描述

3、命令模式的注意事项和细节

1)、将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用;
2)、容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令;
3)、容易实现对请求的撤销和重做;
4)、命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意;
5)、空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦;
6)、命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD (DOS命)订单的撤销/恢复、触发-反馈机制。

三、访问者模式【难】

1、定义

1)、访问者模式,封装一些作用于某些数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作;
2)、主要将数据结构与数据操作分离,解决数据结构操作耦合性问题;
3)、访问者模式的基本工作原理是:在被访问者的类里面加一个对外提供接待访问者的接口;
4)、访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作,同时需要避免让这些操作“污染”这些对象的类,可以选用访问者模式解决;

2、原理类图

在这里插入图片描述

3、代码实现

将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等),请使用访问者模式来说实现
在这里插入图片描述
-上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型

以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码

4、访问者模式的注意事项和细节

优点:
1)、访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
2)、访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据2结构相对稳定的系统
缺点:
1)、具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
2)、违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
3)、因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的。

四、迭代器模式

1、传统设计方案

在这里插入图片描述

问题分析:
1)、将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
2)、实际上我们的要求是 : 在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的遍历的操作
3)、解决方案:=> 选代器模式

2、迭代器基本介绍及原理类图

1)、迭代器模式 (iterator Pattern) 是常用的设计模式,属于行为型模式
2)、如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3)、迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素3)不需要知道集合对象的底层表示,即:不暴露其内部的结构。
在这里插入图片描述
对原理类图的说明-即(法代器模式的角色及职责)

1)、Iterator: 迭代器接口,是系统提供,含义haWext,next,remove
2)、ConcreteIterator :具体的法代器类,管理迭代
3)、Aggregate :一个统一的聚合聚合接口,将客户端和具体聚合解耦
4)、ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历集合
5)、Client :客户端,通过IteratorAggregate 依赖子类

3、案例解析

需求:编写程序展示一个学校院系结构,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。

原理图:
在这里插入图片描述
代码解析:
1、学院及信息的代码展示
在这里插入图片描述
2、迭代器代码
在这里插入图片描述

4、迭代器的注意事项和细节

优点:
1)、提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
2)、隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
3)、提供了一种设计思想,就是一个类应该只有一个引起变化的原因 (叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
4)、当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式;
缺点:
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类

五、观察者模式

1、定义及原理类图

对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化,比如“奶站”是subject是一方,用户Observer是多的一方。
在这里插入图片描述

2、观察者模式的好处

1)、观察者模式设计后,会以集合的方式来管理用户 (Observer),包括注册,移除和通知。
2)、这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核2)心类WeatherData不会修改代码,遵守了ocp原则

3、代码解析

在这里插入图片描述

六、中介者模式

装饰者是动态的给其他对象添加职责的,中介者是承担其他对象职责从而解耦

1、基本介绍

1)、 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互;
2)、中介者模式属于行为型模式,使代码易于维护;
3)、比如MVC模式,c (controller控制器) 是M (Model模型)和V (View视图) 的中介者,在前后端交互时起到了中间人的作用;

2、案例原理图及代码解析

在这里插入图片描述
1、首先注册智能家居到中介者集合中

在这里插入图片描述
2、发出消息获取操作
在这里插入图片描述

3、中介者模式的注意事项和细节

1)、多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构进行解耦
2)、减少类间依赖,降低了耦合,符合迪米特原则
3)、中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
4)、如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意

七、备忘录模式

1、定义

1)、备忘录模式(Memento Pattern) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态;
2)、可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情2或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作;

2、原理及代码即系

在这里插入图片描述
1)、originator:对象(需要保存状态的对象)
2)、Memento:备忘录对象,负责Memento :保存好记录,即Originator内部状态
3)、Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
4)、说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要要 HashMap <String,集合>
在这里插入图片描述

3、备忘录模式的注意事项和细节

1)、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
2)、实现了信息的封装,使得用户不需要关心状态的保存细节
3)、如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意
4)、适用的应用场景:1、后悔药。 2、打游戏时的存档。 3Windows 里的 ctri +z。4)4、IE 中的后退。4、数据库的事务管理
5)、为了节约内存,备忘录模式可以和原型模式配合使用

八、解释器模式【难】

1、问题分析

在这里插入图片描述

2、解释器定义

1)、在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法在编译原理中,单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
2)、解释器模式 (Interpreter Pattern) : 是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
3)、应用场景

  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
  • 一些重复出现的问题可以用一种简单的语言来表达
  • 一个简单语法需要解释的场景
    4)、这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等

3、解释器原理类图

在这里插入图片描述
1)、Context: 是环境角色,含有解释器之外的全局信息.
2)、AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
3)、TerminalExpression: 为终结符表达式,实现与文法中的终结符相关的解释操作
4)、NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作
5)、说明: 输入Context he TerminalExpression 信息通过Client 输入即可

4、代码实现

案例:通过解析器模式来实现四则运算 如a+b-c的值

在这里插入图片描述

思路分析:
1、首先获得表达式;
2、获得值的map映射 例如:var{a=10,b=20};
3、使用Calculator构造器解析表达式 最终获得Expression实例表达式;
4、使用Calculator的run方法 根据不同实例进行运算处理 得到最终答案;

在这里插入图片描述
在这里插入图片描述

5、解析器模式的注意事项和细节

1)、当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
2)、应用场景:编译器、运算表达式计算、正则表达式、机器人等
3)、使用解释器可能带来的问题。解释哭模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.

九、状态模式

1、App抽奖活动问题

在这里插入图片描述

2、状态模式基本介绍

1)、状态模式:它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
2)、当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

3、原理类图

在这里插入图片描述
1)、Context类为环境角色,用于维护State实例,这个实例定义当前状态;
2)、State是抽象状态角色,定义一个接口封装与Context的一个特点接口相关行为;
3)、ConcreteState具体的状态角色,每个子类实现一个与Context的一个相关行为;

4、状态模式解决App抽奖活动问题

在这里插入图片描述

思路分析:
1、先初始化对象RaffleActivity:1.初始化当前的状态为 noRafflleState(即不能抽奖的状态)2. 初始化奖品的数量;
2、参加抽奖:1.扣除积分 2.将状态设置成可抽奖状态;
3、抽奖:1.如果中奖改状态为发放奖品(若奖品数量不为0则发放奖品并改状态为不能抽奖状态,若奖品数量为0则退出)2.如果未中奖,改状态为不能抽奖即可;

在这里插入图片描述
在这里插入图片描述

5、状态模式的注意事项和细节

1)、代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中;
2)、方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句而且容易出错;
3)、符合“开闭原则”。容易增删状态;
4)、会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度;
5)、应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式;

十、策略模式

1、鸭子问题

编写鸭子项目,具体要求如下:

1)、有各种鸭子(比如 野鸭、北京鸭、水鸭等,鸭子有各种行为,比如 叫、飞行等
2)、显示鸭子的信息

2、传统方案解决鸭子问题

在这里插入图片描述
传统的方式实现的问题分析和解决方案

1)、其它鸭子,都继承了Duck类,所以flv让所有子类都会飞了,这是不正确的
2)、上面说的1的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
3)、为了改进1问题,我们可以通过覆盖flv 方法来解决 => 覆盖解决
4)、问题又来了,如果我们有一个玩具鸭子ToyDuck,这样就需要ToyDuck去覆盖Duck4)的所有实现的方法 => 解决思路 `策略模式 (strategy pattern)`

3、策略模式基本介绍

1)、策略模式 (Strategy Pattern) 中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
2)、这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合少用继承 (客户通过组合方式使用策略)。

4、策略模式解决鸭子问题

原理类图:
在这里插入图片描述
代码解析:
在这里插入图片描述

5、策略模式的注意事项和细节

1)、策略模式的关键是:分析项目中变化部分与不变部分
2)、策略模式的核心思想是:多用组合/聚合 少用继承:用行为类组合,而不是行为的继承。更有弹性
3)、体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只3要添加一种策略(或者行为) 即可,避免了使用多重转移语句 if…else if…else)
4)、提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
5)、需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

十一、职责链模式

1、基本介绍

1)、职责链模式(Chain ofResponsibility Pattern),又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
2)、职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

2、原理类图

在这里插入图片描述

3、职责链模式解决OA系统采购审批

编写程序完成学校 OA 系统的采购审批项目: 需求

采购员采购教学器材
如果金额 小于等于 5000,由教学主任审批
如果金额 小于等于 10000,由院长审批
如果金额 小于等于 30000、由副校长审批
如果金额 超过 30000 以上,有校长审批

思路分析和图解
在这里插入图片描述
在这里插入图片描述

4、职责链的注意事项和细节

1)、将请求和处理分开,实现解耦,提高系统的灵活性;
2)、简化了对象,使对象不需要知道链的结构;
3)、性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个3)最大节点数量,在 setNext0方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能;
4)、调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂;
5)、最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat5)对 Encoding 的处理、拦截器;

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java亮小白1997

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值