不变模式
一个类创建后,在整个生命周期内都不会变化。有两种:弱不变模式和强不变模式。
弱不变模式:该类不变,子类有可能变化。1. 引用到其他对象时,在类内部初始化,如果必须在客户端初始化的,则在类内部使用复制方式进行初始化。 实现方法: 不提供set函数
强不变模式: 在满足弱不变模式的情况下,类或方法在子类中不能修改,实现方法:函数为final或类为final
与其他模式关系:
享元模式内部可以有变化,但和环境无关即可
策略模式
作用:
更换整个算法,改为其他方法来解决同样的问题。
模式将行为和环境分开。
应用场景:
多个类的区别只是行为
系统动态地在多个行为间进行选择
策略使用的数据不需要让客户知道
角色:
Strategy:行为类接口,规定API
ConcreteStrategy:具体的行为类
Context:环境部分,维持和查询行为类,有成员对象 strategy
注意:
使用时,考察算法是否可从环境中分割,再考察算法如何变化
使用何种算法由Context决定
如果一次使用多种策略,需要使用装饰模式
优缺点
1.策略模式保证了采取哪种算法的逻辑和算法可以单独变化
2.避免使用多重条件语句
和其他模式关系:
创建模式:创建专注于创建,策略的使用更广一些
享元模式:如果某些策略的区别只是客户状态 context不同,则可以将这些信息抽象出来由客户端注入,从而减少策略类
模板方法:模板使用继承提供不同的算法行为,策略使用委派提供不同的算法行为
装饰模式:是否增强功能, 当策略需要同时使用一个以上的算法时,可同时使用策略和装饰
模板方法
作用:
父类指定处理的骨架,子类规定具体内容(抽象类与实现类)
角色:
AbstractClass父类模板,骨架算法函数使用final修饰
ConcreteClass子类的实现
在重构中的应用
big method: extrract method; 将函数局部变量使用函数返回;将常量使用函数返回
观察者模式(发布和订阅模式)
作用:
给类加入观察者后,当类的状态发生变化,就会通知观察者,在写一些和状态变化有关的处理时,很有用。
观察者类似于事件中的发布订阅模式
角色:
Subject:抽象类,表示被观察者,属性有List<Observer>,函数有增加和删除观察者,通知观察者方法 notify()。
ConcreteSubject:表示实际的观察者,改变内部状态的方法 change。
Observer:表示观察者,有方法update(Subject s)由subject.notify()调用。
ConcreteObserver:表示实际的Observer。调用update方法,可获得ConcreteSubject的当前状态。
优缺点:
1. 松散耦合
2.广播通信
1.观察者之间如果有循环调用,可能会系统崩溃
2.观察者不知道Subject是怎么发生变化的
注意:
使用 java.util 支持的观察者模式
Observer可以存在Subject,或者ConcreteSubject中,优先放在Subject中。
Java支持
java.util中提供了Observable(类似Subject)类和Observer接口, 实现时:
l ConcreteSubject继承Observable类 ,状态变化时setChange()置位;调用notifyObservers 通知Observer;
l ConcreteObserver实现Observer接口,定义update方法,第一个参数是ConcreteSubject
l 客户端:创建Observer和Observable对象;加入Observer:add(Observer); 改变Observable的状态(setChanged(), notifyObservers() 两个都要调用)
l 有多个observer时,确保update方法调用顺序改变时也不会发生问题。
和其他模式关系:
备忘录模式:subject将observer以备忘录模式存储在体内
应用实例:
Tomcat的声明周期管理,如下:
LifecycleListener 代表的是抽象观察者,它定义一个 lifecycleEvent 方法,这个方法就是当主题变化时要执行的 方法。
ServerLifecycleListener 代表的是具体的观察者,它实现了 LifecycleListener 接口的方法。
Lifecycle 接口代表的是抽象主题,它定义了管理观察者的方法和它要所做的其它方法。StandardServer代表的是具体主题,它实现了抽象主题的所有方法。
这里 Tomcat 对观察者做了扩展,增加了另外两个类:LifecycleSupport、LifecycleEvent,它们作为辅助类扩展了观察者的功能。
LifecycleEvent 使得可以定义事件类别,不同的事件可区别处理,更加灵活。
LifecycleSupport 类代理了主题对 多观察者的管理,将这个管理抽出来统一实现,以后如果修改只要修改LifecycleSupport 类就可以了,不需要去修改所有具体主题,因为 所有具体主题的对观察者的操作都被代理给 LifecycleSupport 类了。这可以认为是观察者模式的改进版。
迭代器
作用:
将iterator和Aggregate分开,使得iterator和Aggregate分开变化。比如以前用数组实现,现在改成列表,则用iterator的遍历不用做任何修改
角色:
Iterator:定义遍历元素的接口。定义next()和hasNext()方法
ConcreteIterator:实现iterator所定义的接口,一种是包括ConcreteAggregator对象,另一种是作为ConcreteAggregate的成员类
Aggregate:要访问的集合,定义了iterator()方法
ConcreteAggregate:实现了Aggregate接口,
宽接口和窄接口
宽接口支持修改,窄接口只能访问
ConcreteAggregate为客户端提供窄接口,为ConcreteIterator提供宽接口
实现双重接口的方法:将ConcreteIterator作为ConcreteAggregate的内部成员类
模式的实现:
ConcreteIterator 为单独类:可以被不同的对象共同控制
ConcreteIterator 为成员类:不破坏对聚集的封装
优缺点:
1.一个聚集上可以定义多个Iterator
2.Iterator返回的对象无类型特征(1.5以上可通过泛型支持)
1.Iterator给人一种顺序化的错觉,客户端如果依赖顺序会出错
和其他模式关系:
合成模式:迭代子遍历树结构
命令模式:命令维护历史清单聚集,并使用迭代子遍历历史清单元素
工厂模式:聚集角色提供一工厂方法,向外界提供自己的迭代子对象,如 iterator()
说明:
遍历功能的进一步扩展,如Predicate,Closure等。
java集合
Collection 代表聚集;
List 表示聚集有顺序,所以 get(inex) 在中间增加和删除用户
Set 元素必须唯一
Map
Iterator 对象通过 Collection.iterator() 得到,还有remove()方法
ListIterator 通过 List.listIterator() 得到,支持正向和逆向迭代,set,add(当前的后边)和remove
Collection List Iterator和ListIterator 四个很好地阐述了迭代子模式,使用内部成员类实现
责任链模式
作用:
由多个对象构成链,客户端发出请求时,该请求在链上传递,如果当前对象不能处理继续传递给下一个。
请求端(客户端)并不知道哪一个处理端完成处理。
角色:
Handler:处理端的抽象类,包含函数setSuccesor,handle, 属性 Handler succesor
ConcreteHandler:具体处理类,实现handl函数
Client: 定义多个handler,完成责任链的组装,调用handler1.handle();
注意:
如果是线性单一处理就是责任链模式,如果是线性多个处理就是装饰器模式,如果是单一处理就是策略模式。
使用spring可以更简单的实现,只要在新增加一个HandlerManager类,在其中实现具体的处理逻辑如线性单一,线性多个,单一处理就可以了。
命令只能传给一个处理器
责任链可以是线、树(合成模式),环(next()不为空)
使用场景:
系统有处理者对象组成的链,可能有合成模式构成
多于一个处理器对象,而且哪个处理器完成处理动态确定的
请求者不明确指定那个处理器处理
处理者对象的链是动态指定的
案例:
是 Tomcat 中 Container 设计的基础,整个容器的就是通过一 个链连接在一起,,从Engine 到 Host 再到 Context 一直到 Wrapper 都是通过一个链传递请求,这个链一直将请求正确的传递给最终处理请求的那个 Servlet。
击鼓传花: 击鼓者是Client,传花者是Handler,具体的人是ConcreteHandler, 花是传递的具体数据
和其他模式关系
合成模式:完成处理者的组成
命令模式
作用:
产生请求时,根据请求创建对象实例,按照发生顺序排入等候序列中。接着,根据顺序执行相应的command。
可以将command对象保存起来,下次使用
多个command组成一个新的command(宏命令)
应用场合;
callBack的实现,将invoker传入comand中,command的执行完成回调
接收端可以否决请求或者记录日志,方便后期恢复
较容易设计一命令队列,对请求排队
命令的undo和redo: 需要保存动作(命令)和状态
角色:
Command:命令的interface,方法 execute()
ConcreteCommand:具体的命令,可以是宏命令和一般命令,调用receiver.action()方法
Receiver:接受者,具体执行请求
Client:产生ConcreteCommand和Receiver、Invoker对象,并进行组装; 调用invoker.action() 启动执行。Client知道执行什么(ConcreteCommand)和谁(Receiver)来执行,由谁(Invoker)跟进
Invoker:请求者,调用command.execute()执行请求。
优缺点:
invoker 和 receiver 分开,请求方不知道接收方的接口,更不知道数据如何接受,操作是否完成
非常容易加入新的命令
可能会导致过多的ConcreteCommand类
注意:
宏命令: 将多个命令一次执行: 使用合成模式处理 Command
支持undo和redo:Command提供 unExecute方法可以恢复操作的效果;Receiver保存最初状态数据,并提供方法可以返回到最初状态
如果只是一层的undo和redo,只要存储最后执行的命令。如果是多层的undo和redo,需要存储曾经被执行过的命令清单。
案例:
tomcat:
Connector作为抽象请求者
HttpConnector作为具体请求者。
HttpProcessor作为命令。
Container作为命令的抽象接受者, ContainerBase 作为具体的接受者。
应用服务器 Server 作为Client。
Server首先创建命令请求者 HttpConnector 对象,然后创建命令 HttpProcessor对象。再把命令对象交给命令接受者 ContainerBase 容器来处理命令。命令的最终是被 Tomcat 的 Container 执行的。命令可以以队列的方式进来,Container 也可以以不同的方式来处理请求,如 HTTP1.0 协议和 HTTP1.1 的处理方式就会不同。
西游记:
圣旨是Command,定义执行接口;
宣孙悟空上天的圣旨是ConcreteCommand;
玉皇大帝是client: 创建具体的圣旨和谁来执行, 委托太白金星执行
太白金星是Invoker
孙悟空是 Receiver:完成具体的执行
其他模式关系:
合成模式: 构造宏命令
备忘录模式:redo和undo功能中,备忘录模式存储命令的执行效果状态
原始模式:命令类带有clone的话,可以复制
备忘录模式(Memento)
作用:
不破坏封装前提下,捕捉对象状态,并外部存储。
可利用该模式,执行以下操作:undo redo history(产生操作记录) snapshot(存储当前状态),使用数组存储多个状态。
常与命令模式和迭代模式一起使用。
角色:
Originator:方法 Memento store() 创建Memento,并使用其保存对象状态,load(Memento) 恢复以前状态。
Memento:Originator要保存的内部信息。Memento有俩种接口:wideinterface供Originator使用,提供存取的所有方法。Narrow interface供包外的相关类使用。
Caretaker:函数 save(Memento) 保存对象,只能使用Memento的“狭义接口”。
Client:决定保存、复原和做快照的时间点。创建Originator和Caretaker对象,调用Originator.store()产生Memento,并传递给Caretaker。
变种:
CareTaker增强:可以将创建Originator和调用save方法移入 CareTaker中
将 Caretaker和Originator的角色合并
注意:
多检查点的实现:Caretaker持有map,每个entry为<checkpoint,Memento>,Client传入要返回到的checkpoint
双重接口的实现(以Originator Mentento MententoNarrow Caretaker 为例):
class Originator {
// 作为Originator的内部类实现宽接口
private class Memento implements MementoNarrow {
//除了 MementoNarrow定义的函数,其他都为静态
}
// 外部只能通过这个函数访问
public MementoNarrow store() {
return new Memento();
}
}
备忘录模式操作步骤:
1. Originator 创建一个Memento
2. Originator 执行一个可撤销的操作(多是对自己状态的改变)
3. 如果要撤销,通过 Originator.load(Memento) 撤销改变
假如模式操作步骤:
当恢复Originator比较复杂时使用,可保证Originator永远有效;缺点是操作执行两次,如果拷贝和源对象共享数据就麻烦了。
1.创建Originator的一个拷贝
2.在拷贝上执行某个操作
3.检查拷贝状态是否合法,正确就在Originator上执行,错误则丢弃并抛出异常
和其他模式关系
命令模式:如果命令需要撤销,则命令使用备忘录存储可撤销操作的状态
原始模式:使用原始进行备忘录的创建
优缺点:
1. 简化了Originator类
1. 如果Originator所有信息都要保存,Memento类会比较昂贵
2.Caretaker 保存Memento时,并不知道该对象占用多少存储空间
状态模式
作用:
对象在不同的状态下有不同的行为。
应用场合:
多个方法写同样的条件判断
扩展新的状态不影响已有类
角色:
State:表示状态的interface,规定行为 method。
ConcreteState:实现类,对应一种状态,该状态下的属性和行为
Context:定义客户端感兴趣的接口,具体实现委派给持有的ConcreteState实例,调用其函数。
注意:
具体的状态类使用了单例模型
状态的转换可以由Client Context 和State实现,如果转换条件固定,可使用Context;State子类决定就更有灵活性。
状态对象的创建和销毁: 状态变化频繁,创建成本高时(预先创建);不频繁变更(使用时创建)
可以使用状态图描述对象系统:画出对象的状态图;有几种状态;每种状态下完成的工作(状态和行为组成二维图形)
优缺点:
1。需要为每种状态创建一个ConcreteState,可能会造成类过多
1. 减少大量的判断语句
与其他模式关系:
享元模式:保存状态间公共属性
单例模式:状态唯一
策略模式: 看Context是否有状态切换,在策略模式中,Context的整个生命周期内Strategy不会变化,而状态模式中不同的状态会使用;
访问者模式
作用:
把聚集和处理俩者分开,一个变化不会影响另一个
聚集中元素类型复杂(树形),不同类型有特定的操作(处理细节和元素类型相关,有多个if)
不当应用场合:
1. Node体系频繁发生变化:比如需要实现新的Node子类时,需要在每一个Visitor中加入相应方法。
应用场合:
1.比较稳定的数据结构和 易于变化的算法
角色:
Visitor:接口,声明对元素的访问方法 visit(Node n)
ConcreteVisitor:实现类
Node:聚集中的元素,具有 boolean accept(Visitor v) 返回值表明是否接受visitor的访问
ConcreteNode:实现了accept 方法
ObjectStructrue:包含ConcreteNode的聚集,提供遍历方法。
双重委派:
动态地根据函数所属类和参数选择正确的函数:实现方法:1. 使用 instanceof 对参数 obj类型做判断。 2. 返还球:调用传入参数obj.fun()第二次动态分派
单个元素处理时的双重委派:
NodeA.accept(Visitor v) 中调用VisitorA.visitA(this) , 再调用 NodeA.operationA() 完成(重构的时候可能会出现,否则谁第一次会想这么复杂,而且我觉得不需要设计这么负责,可以根据实际情况简化)。
优缺点:
1.在Node体系中加入新操作比较容易
2.将多个Node都有的行为集中到Visitor中,而不是分散到各个Node类中
1. 增加新的节点类型比较困难
2. 破坏Node的封装,从而访问者模式是一个有争议的模式
与其他模式关系:
迭代子模式: 针对不同元素类是否行为不同
解释器模式
作用:
使用“迷你”语言描述问题,使用“解释器模式”编译成JAVA程序,当程序发生变化时,只需要修改“迷你”语言。如:海龟图的例子。
应用场合:
重复发生的问题可以用这种简单语言表示
效率不是问题
角色:
抽象表达式:接口定义intercept() 该函数对表达式进行解析
终结表达式:实现抽象表达式,不包含其他表达式。如 NUM1+NUM2 中的NUM2,intercept()只需要对自身解析
非终结表达式:实现抽象表达式,本身包含其他的 表达式,如上面的 +,intercept()递归调用
客户端:构造抽象语法树(AST,表达式),调用Intercept函数
Context:提供map完成表达式和真实量值的转换,如NUM1 为124;
和其他模式的关系:
合成模式: 抽象语法树(AST)的表达式就构成一个合成模式(抽象表达式,非终结表达式为树枝,终结表达式为树叶)
不变模式:某些表达式可以设计为不变模式
访问者模式:如果将AST节点中的共有操作放在visitor中
中介者模式
作用:
对对象间的调用进行管理
多个成员中引入经理,彼此之间的交互转变为和经理的交互。
角色:
Mediator:定义同事到Mediator的interface,声明一个或多个事件方法change(Colleague c)。
ConcreteMediator:持有所有的ConcreteColleague对象,实现change,从ConcreteCollegue接收消息,向相应的Colleague发成命令
Colleague:定义Mediator到同事的interface,持有Mediator对象
ConcreteColleague:
特点:
部分交互关系变化不会影响到其他成员
容易实现colleague的扩展,而mediator的扩展不太容易
Colleague作为参量传递到Mediator的事件方法; Mediator在Colleague中是实例变量
优缺点:
1. 将对象在小尺度的行为和与其他对象的协作分开
2. 减少继承的出现
1.同事类的复用是以 Mediator的不可复用为代价的
应用场景:
1.对象责任划分混乱时,应当进行重构而不是使用中介者模式
2.中介者模式是对协作进行封装,不要认为割裂纯数据类和纯行为类
其他模式关系
1.适配器模式: Mediator ConcreteMediator和Colleague构成一适配器
2.门面模式:两者都是简化调用,门面是外对内单向的,中介者是双向的
3.观察者模式:两者是二选一,观察者通过引入主题和观察者解耦协作;中介者引入中介解耦协作。中介者不适于设计一个大型系统的事件处理机制,建议使用观察者,毕竟java支持了。