16. 命令模式
16.1 问题引入
问题引入(智能生活)
16.2命令模式介绍
命令模式介绍
- 命令模式:在开发中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,此时我们只需在程序运行时指定具体的请求接收者即可。
- 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
- 在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命令),同时命令模式也支持撤销的操作。
命令模式的原理类图
命令模式的角色及职务的分析
Invoker:是调用者角色。
Command:是命令角色,需要执行的所有命令都在这里,可以是接口或者抽象类。
Receiver:接收者角色,知道如何实施和执行一个请求相关的操作。
ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute.
16.3 使用命令模式实现问题
使用命令模式解决问题的类图
注意:以上只是针对电灯做了开关,电视等其他的家电要在进行类的创建
16.4 命令模式在JDK中的应用
- Spring框架的jdbc Template使用到了这个设计模式
16.5 命令模式的注意事项与细节
- 将发起请求的对象与执行请求的对象解耦。
- 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。
- 容易实现对请求的撤销和重做。
16.6 命令模式的使用场景
界面的一个按钮都是一条命令、模拟CMD(DOS命令)、订单的撤销/恢复、触发-反馈机制。
17.访问者模式
17.1 问题引入
问题引入(测评系统需求)
17.2 访问者模式介绍
访问者模式简介
- 封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
- 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题。
- 访问者模式的工作原理:在被访问的类里面加一个对外提供接待访问者的接口。
访问者模式的原理类图
访问者模式的角色及职务的分析
- Visitor:是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作。
- ConcreteVisitor:是一个具体的访问值 实现每个由Vistor声明的操作,是每个操作实现的部分
- ObjectStruture:能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素。
- Element:定义一个accept方法,接收一个访问者对象。
- ConcreteElement:为具体元素,实现了 accept 方法。
17.3 使用访问者模式解决问题
使用访问修饰者解决问题的类图
17.4 访问者模式之双分派
- 所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型。
17.5 访问者模式的应用场景
- 需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免这些操作”污染“这些对象的类,可以选用访问者模式。
- 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的。
18.迭代器模式
18.1 迭代器介绍
迭代器的简介
- 迭代器属于行为型模式。
- 如果我们的 集合元素是用不同的方式实现的。有数组、集合等或者其他的存储方式,当客户端要 遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
- 迭代器模式:提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。
迭代器的原理类图
18.2 迭代器模式在JDK中的应用
- 在JDK—ArrayList集合中使用到了迭代器模式
18.3 迭代器模式的注意事项和细节
优点:
- 提供一个统一的方法遍历对象,客户不用在考虑聚合的类型,使用一种方法就可以遍历对象。
- 隐藏了聚合的内部结构,客户端遍历集合的时候只能取到迭代器,而不会知道聚合的具体组成
- 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做 单一职责原则)。
- 当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式。
缺点:
每个聚合对象都要有一个迭代器,会生成多个迭代器。
19.观察者模式
19.1 使用普通方式解决问题
问题引入(天气预报)
解决问题的思路
使用普通方式解决问题分析
- 在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象。并加入到datachange,不利于维护,也不是动态获取的。
19.2 观察者模式介绍
观察者模式原理
19.3 使用观察者模式解决问题
观察者解决问题的类图
19.4 观察者模式在JDK中的应用
- JDK中的Observable类使用到了观察者模式。
20.中介者模式
20.1 使用普通方式解决问题
问题引入(智能家电)
20.2 中介者模式介绍
中介者模式简介
- 中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。
- 中介者模式属于行为型模式,使代码易于维护。
- 比如MVC模式,C(Controller控制器)、M(Model模型)、V(View视图)的中介者,在前后端交互时起到了中间人的作用。
中介者模式的原理类图
简单理解中介者模式的类图
20.2 使用中介者模式解决问题(智能家庭)
实现类图
20.3 中介者模式注意事项与细节
- 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行耦合。
- 减少类间依赖,降低了耦合,符合迪米特法则。
- 中介者模式承担了较多的责任,一旦中介出现了问题,整个系统就会受到影响。
- 如果设计不当,中介者对象本身会变得很复杂,这点在实际开发中,需要注意。
21.备忘录模式
21.1 使用普通方式解决问题
问题引入(游戏角色状态)
普通方案解决类图
普通解决方案问题分析
- 一个对象,就对应一个保存对象状态的对象,这样当我们游戏的对象很多时,不利于管理,开销较大。
- 传统的方式是简单地做备份,new出另一个对象出来,再把需要备份的数据放到这个新的对象,但是这就暴露了对象内部的细节。
21.2 备忘录模式介绍
备忘录模式简介
- 备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
- 可以这样理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情或者是记录已经达成的共同意见的事情,以防止忘记。而在软件开发中,备忘录模式有相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。
- 备忘录模式属于行为型模式。
备忘录模式的原理类图
备忘录模式的角色及职务的分析
- originator:对象(需要保存状态的对象)。
- Memento:备忘录对象,负责保存好记录,即Originator内部状态。
- Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率。
21.3 使用备忘录模式解决问题
备忘录模式的类图
21.4 备忘录模式的注意事项和细节
- 给用户提供一种可以恢复状态的机制,可以使用户能够比较方便地找回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
- 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
21.5 备忘录模式的适用场景
- 后悔药。
- 打游戏时的存档。
- Windows中的 ctrl+z(撤销)。
- IE中的后退。
- 数据库的事务管理。
为了节约内存,备忘录模式可以和原型模式配合使用
22.解释器模式
22.1 使用传统方式解决问题
问题引入(四则运算)
22.2 解释器模式介绍
解释器模式简介
- 在编译原理中,一个算数表达式通过 词法分析器形成词法单元,而后这些词法单元再通过 语法分析器构建语法树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看作是解释器。
- 解释器并不是我们想象的一个简单的翻译器,解释器的内部结构非常饿复杂。可能含有多个解释器类,这些类之间相互关联。
- 计时器模式:是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
解释器模式的原理类图
解释器模式的角色及职务的分析
22.3 使用解释器模式来实现四则表达式
解决问题:计算a+b-c 的数字
使用解释器解决为问题的类图
22.4 解释器模式在JDK中应用
- Spring中的 SpelExpressionParse使用到了解释器模式
22.6 解释器模式的注意事项和细节
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象的语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性。
- 使用解释器模式可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。
22.5 解释器的应用场景
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语句来表达。
- 一个简单语法需要解释的场景。
- 编译器、运算表达式计算、正则表达式、机器人等。
23. 状态模式
23.1 使用传统的方式解决问题
问题引入(APP抽奖活动)
23.2 状态模式的介绍
状态模式的简介
- 状态模式:它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的关系,状态之间可以相互转换。
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。
状态模式的原理类图
状态模式的角色及职务的分析
- Context 类:为环境角色,用于维护State实例,这个实例定义当前状态。
- State接口:是抽象状态角色,定义一个接口疯转与Context的一个特点接口相关行为。
- ConcreteState类:是具体的状态角色,每一个子类实现一个与Context的一个状态相关行为。
23.3 使用状态模式解决抽奖问题
使用状态模式解决问题的类图
23.4 状态模式的注意事项和细节
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
- 方便维护。不在需要使用if-else()语句来完成。
- 符合”开闭原则“。容易增删状态。
- 会产生很多的类。每一个状态都有一个对应的类,当状态过多时会产生很多的类,加大维护难度。
23.5 状态模式的应用场景
当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。
24. 策略模式
24.1 使用普通方式解决问题
问题引入(鸭子问题)
普通方式解决问题的类图
在Duck类(抽象类或者接口)中给定所有鸭子的共同行为,让不同的鸭子继承这个Duck,不同的鸭子可能还有不同的行为,让不同的鸭子自己实现。
使用普通方式问题分析
24.2 策略模式介绍
策略模式简介
- 策略模式:策略模式中,定义算法簇,分别封装起来,让他们之间可以相互替换,次模式让算法的变化独立于使用算法的客户。
- 这算法体现了几个设计原则:
- 把变化的代码从不变的代码中分离出来。
- 针对接口编程而不是具体的类(定义了策略接口)。
- 多用组合/聚合,少用继承(客户通过组合方式使用策略)。
策略模式的类图
24.3 使用策略模式解决问题
思路分析与类图
策略模式:分别封装行为接口,实现算法簇,超类里放行为接口对象,在子类里具体设定行为对象。原则是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者。
24.4 策略模式在JDK中的应用
- Arrays的Comparator就使用了策略模式
24.5 策略模式的注意事项以及细节
- 策略模式的关键:分析项目中变化部分与不变部分。
- 策略模式的核心思想:多用组合/聚合,少用继承;用行为类组合,而不是行为的继承。
- 体现了“对修改关闭,对扩展开放”原则。客户端增加行为不需要修改以前的代码,只需添加一种策略(或者行为)即可,避免了使用多重转移语句(if...else())。
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多时会导致类的数目庞大。
25.职责链模式
25.1 使用普通方式解决问题
问题引入(OA系统采购审批)
普通方式实现问题的类图
普通方式实现问题分析
25.2 职责链模式介绍
职责链模式简介
- 职责链模式又叫 责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者就行解耦。
- 职责链通常每个接收者都包含另一个接收者的引用。如果一个对象不能处理该请求,那么他会把相同的请求传递给下一个接收者,依此类推。
- 这种设计模式属于行为型模式
职责链模式的原理类图
25.3 职责链模式在JDK中的应用
- 在SpringMVC中的HandlerExecution类使用到了职责链模式。
25.4 职责链模式的注意事项和细节
- 讲请求和处理分开,实现解耦,提高系统的灵活性。
- 简化了对象,使对象不需要知道链的结构。
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。
25.5 职责链的应用场景
- 有多个对象可以处理同一个请求,比如:多级请求、请假/加薪等审批流程、JavaWeb中Tomcat对Encoding的处理、拦截。