设计模式(四)行为型
设计模式(行为型)
前面我们已经学习了12种设计模式,分为两类:
- 创建型:关注对象创建
- 结构型:关注类和对象的结构组织
我们接着来看最后一种设计模式,也是最多的一种,行为型设计模式关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责。
解释器模式
这种模式的使用场景较少,很少使用的一种设计模式,这里提一下就行。
解释器顾名思义,就是对我们的语言进行解释,根据不同的语义来做不同的事情,比如我们在SE中学习的双栈计算器,正是根据我们输入的算式,去进行解析,并根据不同的运算符来不断进行计算。
比如我们输入:1+2*3
那么计算器就会进行解析然后根据语义优先计算2*3的结果然后在计算1+6最后得到7,详细实现请参考JavaSE篇双栈计算器实现。
模板方法模式
模板方法模式我们之前也见到过许多,我们先来看看什么是模板方法。
有些时候,我们的业务可能需要经历很多个步骤来完成,比如我们生病了在医院看病,首先是去门诊挂号,然后等待叫号,然后是去找医生看病,确定病因后,就根据医生的处方去前台开药,最后付钱。这一整套流程看似是规规矩矩的,但是在这其中,某些步骤并不是确定的,比如医生看病这一步,由于不同的病因,可能会进行不同的处理,最后开出来的药方也会不同,所以,整套流程中,有些操作是固定的,有些操作可能需要根据具体情况而定。

image-20230301112153648
在我们的程序中也是如此,可能某些操作是固定的,我们就可以直接在类中对应方法进行编写,但是可能某些操作需要视情况而定,由不同的子类实现来决定,这时,我们就需要让这些操作由子类来延迟实现了。现在我们就需要用到模板方法模式。
我们先来写个例子:
现在我们定义好了抽象方法,只是将具体的流程先定义出来了,但是部分方法需要根据实现决定:
这样,我们就有了一个具体的实现类,并且由于看病的逻辑已经由父类定义好了,所以子类只需要实现需要实现的部分即可,这样我们就实现了简单的模板方法模式:

image-20230301112206668
最后我们来看看在JUC中讲解AQS源码实现中出现的代码:
模板方法模式,实际上部分功能的实现是在子类完成的:
是不是现在感觉,这种层层套娃的写法,好像并不是这些大佬故意为了装逼才这样写的,而是真的在遵守规范编写,让代码更易懂一些,甚至你现在再回去推一遍会发现思路非常清晰。当然,除了这里之外,还有很多框架都使用了模板方法模式来设计类结构,还请各位小伙伴自行探索。
责任链模式
责任链模式也非常好理解,比如我们的钉钉审批,实际上就是一条流水线一样的操作,由你发起申请,然后经过多个部门主管审批,最后才能通过,所以你的申请表相当于是在一条责任链上传递。当然除了这样的直线型责任链之外,还有环形、树形等。

image-20230301112223069
实际上我们之前也遇到过很多种责任链,比如JavaWeb中学习的Filter过滤器,正是采用的责任链模式,通过将请求一级一级不断向下传递,来对我们所需要的请求进行过滤和处理。

image-20230301112235576
这里我们就使用责任链模式来模拟一个简单的面试过程,我们面试也是一面二面三面这样走的流程,这里我们先设计一下责任链上的各个处理器:
因为面试有很多轮,所以我们这里创建几个处理器的实现:
这样我们就编写好了每一轮的面试流程,现在我们就可以构建一个责任链了:
可以看到最后结果也是按照我们的责任链来进行的。
命令模式
大家有没有发现现在的家电都在趋向于智能化,通过一个中央控制器,我们就可以对家里的很多电器进行控制,比如国内做的比较好的小米智能家居系列,还有Apple的HomeKit等,我们只需要在一个终端上进行操作,就可以随便控制家里的电器。

image-20230301112308454
比如现在我们有很多的类,彩电、冰箱、空调、洗衣机、热水器等,既然现在我们要通过一个遥控器去控制他们,那么我们就需要将控制这些电器的指令都给设计好才行,并且还不能有太强的关联性。
所有的电器肯定需要通过蓝牙或是红外线接受遥控器发送的请求,所以所有的电器都是接收者:
接着我们要控制这些电器,那么肯定需要一个指令才能控制:
最后我们来安排一个遥控器:
比如现在我们创建一个空调,那么它就是作为我们命令的接收者:
现在我们创建一个开启空调的命令:
最后我们只需要通过遥控器发送出去就可以了:
通过这种方式,遥控器这个角色并不需要知道具体会执行什么,只需要发送命令即可,遥控器和电器的关联性就不再那么强了。
迭代器模式
迭代器我们在JavaSE篇就已经讲解过了,迭代器可以说是我们学习Java语言的基础,没有迭代器,集合类的遍历就成了问题,正是因为有迭代器的存在,我们才能更加优雅的使用foreach语法。
回顾我们之前使用迭代器的场景:
编译之后的代码如下:
可以看到,当我们使用迭代器对List进行遍历时,实际上就像一个指向列表头部的指针,我们通过不断向后移动指针来依次获取所指向的元素:

image-20230301112320947

image-20230301112329551
这里,我们依照JDK提供的迭代器接口(JDK已经为我们定义好了一个迭代器的具体相关操作),也来设计一个迭代器:
现在我们就可以将数据存放在此集合中了:
接着我们就可以来实现迭代器接口了:
接着,我们就可以对我们自己编写的一个简单集合类进行迭代了:
最后编译出来的样子:
这样我们就实现了一个迭代器来遍历我们的元素。
中介者模式
在早期,我们想要和别人进行语音聊天,一般都是通过电话的方式,我们通过拨打他人的电话号码,来建立会话,不过这样有一个问题,比如我现在想要通知通知3个人某件事情,那么我就得依次给三个人打电话,甚至还会遇到一种情况,就是我们没有某个人的电话号码,但是其他人有,这时还需要告知这个人并进行转告,就很麻烦。

image-20230301112503997
但是现在我们有了Facetime、有了微信,我们可以同时让多个人参与到群通话中进行群聊,这样我们就不需要一个一个单独进行通话或是转达了。实际上正是依靠了一个中间商给我们提供了进行群体通话的平台,我们才能实现此功能,而这个平台实际上就是一个中间人。又比如我们想要去外面租房,但是我们怎么知道哪里有可以租的房子呢?于是我们就会上各大租房APP上去找房源,同样的,如果我们现在有房子需要出租,我们也不知道谁会想要租房子,同样的我们也会把房子挂在租房APP上展示,而当我们去租房时或是出租时,就会有一个称为中介的人来跟我们对接,实际上也是一种中介的模式。
在我们的程序中,可能也会出现很多的对象,但是这些对象之间的相互调用关系错综复杂,可能一个对象要做什么事情就得联系好几个对象:

image-20230301112515239
但是如果我们在这中间搞一个中间人:

image-20230301112526146
这样当我们要联系其他人时,一律找中介就可以了,中介存储了所有人的联系方式,这样就不会像上面一样乱成一团了。这里我们就以房产中介的例子来编写:
接着就是用户了,用户有两种角色,一种是租房,一种是出租:
现在我们来测试一下:
中介者模式优化了原有的复杂多对多关系,而是将其简化为一对多的关系,更容易理解一些。
备忘录模式
2021年10月1日下午,河南驻马店的一名13岁女中学生,因和同学发生不愉快喝下半瓶百草枯。10月5日,抢救四天情况恶化,家属泣不成声称“肺部一个小时一变”。10月6日下午,据武警河南省总队医院消息,“目前女孩仍在医院救治。”
喝下百草枯,会给你后悔的时间,但是不会给你后悔的机会(百草枯含有剧毒物质,会直接导致肺部纤维化,这是不可逆的,一般死亡过程在一周左右,即使家里花了再多的钱,接受了再多的治疗,也无法逆转这一过程)相信如果再给这位小女孩一次机会,回到拿起百草枯的那一刻,一定不会再冲动地喝下了吧。

image-20230301112608808
备忘录模式,就为我们的软件提供了一个可回溯的时间节点,可能我们程序在运行过程中某一步出现了错误,这时我们就可以回到之前的某个被保存的节点上重新来过(就像艾克的大招),我们平时编辑文本的时候,当我们编辑出现错误时,就需要撤回,而我们只需要按下
Ctrl+Z
就可以回到上一步,这样就大大方便了我们的文本编辑。其实备忘录模式也可以应用到我们的程序中,如果你学习过安卓开发,安卓程序在很多情况下都会重新加载
Activity
,实际上安卓中Activity
的onSaveInstanceState
和onRestoreInstanceState
就是用到了备忘录模式,分别用于保存和恢复,这样就算重新加载也可以恢复到之前的状态。这里我们就模拟一下对象的状态保存:
接着我们需要保存它在某一时刻的状态,我们来编写一个状态保存类:
接着我们来将状态的保存和恢复操作都实现一下:
现在我们来测试一下吧:
可以看到,虽然在学习Java的过程中,中途摆烂了,但是我们可以时光倒流,回到还没开始摆烂的时候,继续学习Java:

image-20230301112626822
不过备忘录模式为了去保存对象的状态,会占用大量的资源,尤其是那种属性很多的对象,我们需要合理的使用才能保证程序稳定运行。
观察者模式
牵一发而动全身,一幅有序摆放的多米诺骨牌,在我们推到第一个骨牌时,后面的骨牌会不断地被上一个骨牌推倒:

image-20230301112702467
在Java中,一个对象的状态发生改变,可能就会影响到其他的对象,与之相关的对象可能也会联动的进行改变。还有我们之前遇到过的监听器机制,当具体的事件触发时,我们在一开始创建的监听器就可以执行相关的逻辑。我们可以使用观察者模式来实现这样的功能,当对象发生改变时,观察者能够立即观察到并进行一些联动操作,我们先定义一个观察者接口:
接着我们来写一个支持观察者的实体类:
接着我们就可以测试一下了:
这样,我们就简单实现了一下观察者模式,当然JDK也为我们提供了实现观察者模式相关的接口:
我们来测试一下吧:
状态模式
在标准大气压下,水在0度时会结冰变成固态,在0-100度之间时,会呈现液态,100度以上会变成气态,水这种物质在不同的温度下呈现出不同的状态,而我们的对象,可能也会像这样存在很多种状态,甚至在不同的状态下会有不同的行为,我们就可以通过状态模式来实现。

image-20230301112716993
我们来设计一个学生类,然后学生的学习方法会根据状态不同而发生改变,我们先设计一个状态枚举:
接着我们来编写一个学生类:
我们来看看,在不同的状态下,是否学习会出现不同的效果:
状态模式更加强调当前的对象所处的状态,我们需要根据对象不同的状态决定其他的处理逻辑。
策略模式
对面卡兹克打野被开了,我们是去打小龙还是打大龙呢?这就要看我们团队这一局的打法策略了。

image-20230301112727567
我们可以为对象设定一种策略,这样对象之后的行为就会按照我们在一开始指定的策略而决定了,看起来和前面的状态模式很像,但是,它与状态模式的区别在于,这种转换是“主动”的,是由我们去指定,而状态模式,可能是在运行过程中自动切换的。
其实策略模式我们之前也遇到过,比如线程池的拒绝策略:
可以看到,我们如果使用AbortPolicy,那么就是直接抛出异常:

image-20230301112805739
我们也可以使用其他的策略:
这种策略就会从等待队列中踢出一个之前的,不过我们这里的等待队列是没有容量的那种,所以会直接炸掉:

image-20230301112814947
至于具体原因,可以回去看看JUC篇视频教程。
再比如我们现在有一个排序类,但是根据不同的策略,会使用不同的排序方案:
现在我们编写一个排序类:
现在我们就可以指定不同的策略进行排序了:
访问者模式
公园中存在多个景点,也存在多个游客,不同的游客对同一个景点的评价可能不同;医院医生开的处方单中包含多种药元素,査看它的划价员和药房工作人员对它的处理方式也不同,划价员根据处方单上面的药品名和数量进行划价,药房工作人员根据处方单的内容进行抓药,相对于处方单来说,划价员和药房工作人员就是它的访问者,不过访问者的访问方式可能会不同。

image-20230301112827540
在我们的Java程序中,也可能会出现这种情况,我们就可以通过访问者模式来进行设计。
比如我们日以继夜地努力,终于在某某比赛赢得了冠军,而不同的人对于这分荣誉,却有着不同的反应:
我们首先定义一个访问者接口:
然后就是访问者相关的实现了:
可以看到,这里我们就设计了四种访问者,但是不同的访问者对于某一件事务的处理可能会不同。访问者模式把数据结构和作用于结构上的操作解耦,使得操作集合可相对自由地演化,我们上面就是将奖项本身的属性和对于奖项的不同操作进行了分离。
————————————————
版权声明:本文为柏码知识库版权所有,禁止一切未经授权的转载、发布、出售等行为,违者将被追究法律责任。
原文链接:https://www.itbaima.cn/document/5434a3cyyjvwhs8s
Loading...