组合模式(合成模式)
- 定义:
将对象合成树形结构以表示 整体-部分 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 - 自我理解:
用一系列的数据生成树形结构(整体-部分)。例如,给你公司的花名册,现在要将花名册以公司架构的形式展示。即,某部门,某部门下某些员工。 -
类图:
- Component
抽象组合对象,定义了所有组合对象的共有方法和属性。 - Leaf
叶子对象,类似于公司的基本员工,不管理任何人。 - Composite
树枝结构(branch),可以理解成管理层。方便管理,根节点也是它。其下有叶子对象。
Summary
- 优点
1、高层模块调用简单。
只要找到一个其中的一个节点,可以找到root节点。不必关心整体结构。
2、及诶单自由增加
想增加branch就增加branch,想增加leaf就增加leaf。 - 缺点
1、例如上面的leaf 和composite 是一个具体的实现类,限制了接口的影响范围。 - 场景
1、维护和展示 部分-整体关系的场景。eg:树形菜单,文件和文件夹。
2、从一个整体中能够独立出部分模块或者功能的场景。
扩展
组合模式有两种不同的实现:透明模式、安全模式。
- 安全模式
上面的类图就是安全模式。 -
透明模式
类图
个人理解:
将树枝和叶结构统一成一个Component,然后根据其中的条件来判断是叶子结构还是树枝结构。比如:getChild(),返回一个迭代器,如果迭代器是null或者数量为0,那就说明这个结构是一个叶子结构。反之就是一个树枝结构。
观察者模式(发布订阅模型)
- 定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所依赖于他的对象都会得到通知并被自动更新。
- 简单理解:开关 与 灯的亮暗。灯是依赖于开关的,当开关的状态发生改变,灯的亮暗也随之发生改变。
-
类图
- subject
被观察者对象,上面的开关。该类的要求是能够动态的增减观察者,同时能够进行通知。 - observer
观察者,上面的灯,根据被观察者的行为,做出相应的操作。
Summary
- 优点
1、观察者和被观察者之间是抽象耦合
2、建立一套触发机制 - 缺点
1、效率需要考虑。 - 注意
1、广播链(被观察者==》观察者==》观察者==》观察者)这种情况的时候,依赖严重不建议这样建立关系。建议最多只有两层关系(被观察者==》观察者==》观察者)。
2、异步处理问题,单一对多观察的时候,考虑耗时,可能需要开异步。这个时候需要考虑队列以及线程安全。可以参考Message Queue,加深理解。 - 扩展
1、Observer 和 Observable Java中有现成的接口直接实现就可以工作了。
门面模式(外观模式)
- 定义:
要求一个子系统的外部与其内部的通行必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。 -
自我理解:
门面可以理解为一个封装集合,他定义的固定的对外接口,其内部无论怎么变化,提供给外部的方法不能随意变动。简称金玉其外败絮其内。
- Facade门面角色
子系统的封装集合,没有实际的业务逻辑。只是一个委托类。 - 子系统角色
可以同时有个或者多个,每个子系统类是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另一个客户端而已。
Summary
- 优点
1、减少了系统的相互耦合
因为将子系统封装了,所以外部访问不会深入到子系统中的具体方法,减少了耦合。
2、提高了灵活性
子系统随便更改,只要保证提供门面的方法不变,上级依赖稳定。
3、提高安全性
相当于增加了一道防火墙,防止直接访问子系统。 - 缺点
1、不符合开闭原则,对修改关闭,对扩展开放。门面出现问题之后,好像就玩不动了。这就是因为对修改关闭原因。 - 场景
1、为复杂模块或者子系统提供访问的接口
2、子系统相对独立——外界对子系统的访问只要黑箱操作即可
3、预防低水平人员带来的风险,即开发模块化,模块间依赖单一,别人水平低不会影响到你(其实你水平更低)。 - 注意
1、一个子系统可以有多个门面
2、(重点)门面不参与子系统内的业务逻辑
就是门面中的方法不处理业务逻辑。
eg:
“public class Facade {
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
public void methodC(){
this.a.doSomethingA();
this.c.doSomethingC();
}
}”
这种情况下,正确的做法是将A、C进行一次封装,生成新类,同事生成新方法。如下
“public class Context {
//委托处理
private ClassA a = new ClassA();
private ClassC c = new ClassC();
//复杂的计算
public void complexMethod(){
this.a.doSomethingA();
this.c.doSomethingC();
}
}”
“public class Facade {
//被委托的对象
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private Context context = new Context();
//提供给外部访问的方法
public void methodA(){
this.a.doSomethingA();
}
public void methodB(){
this.b.doSomethingB();
}
public void methodC(){
this.context.complexMethod();
}
}”
蛋疼是蛋疼,设计思想就是这样的。
备忘录模式
- 定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在改对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。 - 自我理解
打单机游戏的时候,经常用到的SL大法(Save-Load)。简单来说,将某一个状态保存,方便历史恢复。 -
类图
- Originator
数据源 - Memento
负责存储数据源的数据状态,生成备忘录。 - Caretaker
对备忘录进行管理的类。
拓展
-
Clone方式的备忘录
记得之前的原型模式中用到的Clone,将当前的类进行Clone产生一个相同状态的类。
类图
操作
让originator 实现Clone接口,然后生成备忘录的时候,直接返回克隆对象给备忘录管理类。
注意:采用的是Clone接口,所以还是存在Clone 的弊端,如果成员变量有非原始数据类型的,需要进行深度拷贝。
-
多状态备忘录模式
背景
当Originator属性成员过多的时候,在手动赋值还原的话容易出错,而且也是机械重复操作过多。为了解决这个问题,需要对bean的描述进行解析。
类图
新增一个BeanUtil类进行属性数据的提取和还原。
注意
1、通过bean描述提取属性的时候,读取的时候是根据方法名开头以get来过滤的,所以,写代码的时候需要住,反之出现重复调用,行成死循环。
2、成员变量中,保存的是引用地址,所以会出现类似深度拷贝,和浅拷贝的问题。对于像list的非原始数据额类型,需要单独clone 赋值。
= 多备份的备忘录
背景
历史备份不仅可以有一份备份,也可以有多份备份。所以这种情况下,就需要使用多备份的备忘录。核心是通过Hashmap 生成关键字对应的备忘录,进行管理调用还原。
类图
优化
让备忘录实现一个空接口,之后以空接口作为上层代码的引用。反之数据暴露。
以上四种实现相关代码==》个人github
Summary
- 应用场景
需要保存和恢复的应用场景。入历史回滚。
需要监控副本场景
jdbc(数据库调用)的本质就是备忘录。
访问者模式
- 定义:
封装一些作用于某种数据结构中的各元素操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。 -
自我理解
留一个接口出来,用于扩展数该数据结构的元素操作。
- visitor
抽象访问者,抽象接口或者抽象类,声明访问哪些元素。 - ConcreteVisitor
具体访问者实现类。 - Element
抽象元素,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义。 - ConcreteElement
具体元素,被操作的对象 - ObjectStruture
元素产生者,可以理解为元素的容器或者元素管理者。
Summary
- 优点
符合单一职责原则
扩张性优秀、灵活性高 - 缺点
访问者能够关注到被访问者的内部细节,跟迪米特法则相违背。
违背依赖倒置原则,因为访问者所依赖的 是具体的实现类而不是一个接口或者抽象类。 - 场景
1、需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作污染这些对象的类
2、使用迭代器遍历对象,这个时候还需要做个性操作,这种情况就可以使用访问者模式。 - 扩展
1、统计功能,对需要计数操作的遍历的一种拖拽。
2、多访问者,添加多个访问者。
3、双分派
状态模式
-
引子
从电梯的状态操作开始说起,电梯有四种状态,停止、运行、开门、关门。同一种状态下有多中操作,但是这些操作是否被允许视具体情况而定。
这种情况可以用根据每种情况然后用switch来搞定,如果状态很多的情况,可能代码量很大,而且逻辑很能比较混乱。这里就引出出了状态模式。平时写代码还是喜欢直接上switch,还是觉得用模式麻烦。
定义
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。-
自我理解
还是看上面的引子吧,定义说得自己都云里雾里的。
State ——抽象状态角色
负责对象状态定义,并且封装环境角色以实现状态切换。ConcreteState ——具体状态角色
两个职责:
1、状态变更
2、行为管理Context ——环境角色
定义客户端需要的接口,并且负责具体状态的切换。
Summary
优点
结构清晰,清晰是清晰觉得麻烦好多
遵循设计原则(废话)
封装性非常好缺点
当状态多、方法多的时候类非常庞大。场景
1、行为会随着状态改变而改变的场景
2、条件、分支判断语句的替代者注意
状态数量最好不要超过5个。