您的位置:首页 > 其它

设计模式-行为模式

2004-11-26 17:46 656 查看
1. Chain of Responsibility模式
a) 结构:



注: 1)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理她为止。

b) 优点:
i. 降低耦合程度。每个对象仅需知道如何将请求往后转发。(每个对象只需保留一个后继的引用)
ii. 在职责分发方面提供了额外的灵活性,可以在运行期间对链和职责进行修改。(通过修改后继引用,动态调整链)
iii. 当没有一个对象能处理某个请求时,则最后一个对象丢弃该请求。(提供对请求的默认处理)

c) 使用环境:
i. 有多个对象可以处理一个请求时,哪个对象处理该请求,在运行时刻自动确定。
ii. 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
iii. 可处理一个请求的对象集合应被动态指定。

d) 代码实例:
public abstract class Handler{
protected Handler successor;
public abstract void handleRequest();
}

public class HandlerA extends Handler{
public void handleRequest(){
if(can handle){
dosomething;
}else{
successor.handleRequest();
}
}
}

public class HandlerB extends Handler{
public void handleRequest(){
if(can handle){
doanotherthing;
}else{
successor.handleRequest();
}
}
}

e) 小结:
i. 要自己维护后继者链。
ii. 对于请求,可以用硬编码表示,或者把请求封装起来。
iii. 帮助系统就是一个Chain of Responsibility的一个应用。

2. Command模式
a) 结构:



注: 1)将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

b) 优点:
i. 把请求的处理封装成一个特定的类,而不是通过一串if-else语句进行判定和指派。(JDK1.2中的事件模型是通过if-else实现,从J2SDK开始,事件模型是采用Command模式的形式)
ii. 可记录请求序列,实现undo操作。(由于是在Invoker触发事件,所以,可以在Invoker处对所发生过的事件进行记录。或者通过提供一个全局的事件日志,每当Command执行其execute()方法时,都先在该日志中进行填写,实现请求队列的记录)

c) 使用环境:
i. 在不同的时刻指定,排列和执行请求。一个Commmand对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程,并在那儿实现该请求。
ii. 支持取消操作。(在Command内部,在execute方法的开始部分,进行状态的记录)
iii. 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。(因为记录了请求队列,所以可以进行重做)
iv. 用构建在原语操作上的高层操作构造一个系统,这样一种结构在支持事务的信息系统中很常见。一个事务封装了对数据的一组变动,Commmand模式提供了对事务进行建模的方法。Commmand有一个公共的接口,使得你可以用一种方式调用所有的事务。同时,使用该模式也易于添加新事务以扩展系统。

d) 代码实例:
public class Invoker{
private Command[] commands;
public void onEvent(Event e){
if(e){
commands[i].execute();
}
}
}

public interface Command{
public void execute();
}

public class Receiver{
public void action(){}
}

public class CommandA implements Command{
private Receiver rec;
public void execute(){
rec.action();
}
}

e) 小结: i. Command模式可能导致生成很多小型的类。(可采用内部类解决)
ii. Java事件模型是Command模式的一个应用,其中的ActionListener相当于上述中的Receiver。

3. Iterator模式
a) 结构:



注: 1)提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。

b) 优点:
i. 在不需要暴露集合的结构的情况下,提供一种遍历集合的方法。(由于是由集合负责创建Iterator,所以,只有Iterator知道集合的内部结构,而这个集合的结构对用户是透明的)

c) 使用环境:
i. 访问一个集合对象的内容,而无需暴露她的内部表示。
ii. 支持对聚合对象的各种遍历。
iii. 为遍历不同的聚合结构提供一个统一的接口。(所有的操作都定义在Iterator中)

d) 代码实例:

public interface Aggregate{
public Iterator createIterator();
public int size();
public Node getNode(int index);
}

public interface Iterator{
public void first();
public void next();
public boolean isDone();
public Node current();
}

public class AggregateA implements Aggregate{
public Iterator createIterator(){
return new IteratorA(this);
}
}

public class IteratorA implements Iterator{
private Aggregate agg;
private int current;
private int size;
public void first(){
current = 0;
}
public void last(){
current = size - 1;
}
public void isDone(){
if(current == size)
return true;
else
return false;
}
public Node current(){
return agg.getNode(current);
}
}

e) 小结:
i. 谁控制该遍历。一般有两种方法。一是由客户程序负责,另一种是由遍历器负责。前一种方式比较灵活,但是代码重用的几率小,且耦合度大。后一种灵活性虽然不如前一种,但他可以方便的重用,优化,而且降低了客户程序与集合间的耦合度。
ii. 谁定义遍历算法。一般有两种选择。一是由集合自身定义遍历算法,然后集合把状态保存到遍历器中,另一种是由遍历器负责。前一种方法,便于保存集合的私有信息,不向遍历器暴露。后一种方法,比较灵活,而且相同的遍历算法可以在不同的集合上重用。
iii. 处理同步操作。当一个遍历器在遍历集合时,如果对集合进行了增删操作,可能会导致重复访问,或者少访问了某个集合的元素,所以,需要提供一种手段来保证不发生以上情况。
iv. 空遍历器。当对Composite对象进行递归遍历时。通过在叶子节点提供一个空遍历器(isDone方法总是返回true),即可完成对树形集合的遍历访问。
v. Java的Util包中的Iterator是对该模式的一个应用。

4. Mediator模式
a) 结构:



注: 1)用一个对象来封装一系列的对象交互。Mediator使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。

b) 优点:
i. 降低了对象之间的耦合,将对象的行为局部化在对象内部。(所有Colleague的通信都集中到Mediator当中)
ii. 通过修改Mediator的代码,可以很容易改变对象间的交互。
iii. 可以很容易的在系统中增加新的Colleague,而不需要改变已有的代码。(Colleague只与Mediator通信,他们之间互相不知道)
iv. Mediator解决了Command对象在执行代码时需要过多的了解其他对象的细节的问题。

c) 使用环境:
i. 一组对象以定义良好,但是复杂的方式进行通信,产生的相互依赖关系结构很乱且难以维护。(Mediator用于解耦对象间的通信)
ii. 一个对象引用其他很多对象,并且直接与这些对象通信,导致难以复用该对象。
iii. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

d) 代码实例:
public interface Mediator{}

public class MediatorA implements Mediator{
private Colleague a;
private Colleague b;
public Mediator(){
a = new ColleagueA(this);
b = new ColleagueB(this);
}
public void doSomething(Colleague c){
b.doAnotherthing();
}
}
public abstract class Colleague{
protected Mediator m;
public Colleague(Mediator m){
this.m = m;
}
}

public class ColleagueA extends Colleague{
public ColleagueA(Mediator m){
super(m);
}
public void doSomething(){
m.doSomething(this);
}
}

public class ColleagueB extends Colleague{
public ColleagueB(Mediator m){
super(m);
}
public void doAnotherthing(){

}
}

e) 小结:
i. Mediator模式可以减少子类生成。她把各Colleague中负责通信的部分集中到一起,改变这些行为只需生成Mediator的子类即可,这样各Colleague类可被重用。
ii. 她将各Colleague子类进行解耦。
iii. 她简化了对象协议。从原来可能的n-n改成1-n.
iv. 她对对象间的协作进行了抽象。
v. 她使控制集中化。
vi. Facade模式也是对系统进行抽象,从而提供一个方便的接口,但是Facade模式的通信是单向的,而Mediator是双向的。
vii. 自动机模型可以考虑采用Mediator模式来实现。

5. Observer模式
a) 结构:



注: 1)定义对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知,并被自动更新。

b) 优点:
i. 定义了一个抽象的对象间通信框架。

c) 使用环境:
i. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面,将这两者封装在独立的对象中以使他们可以各自独立地改变和复用。(这方面类似于Builder模式和Bridge模式。Builder模式用于对象生成,Bridge模式用于抽象出通用接口,Observer模式用于对状态改变进行通知)
ii. 当对一个对象地改变需要同时改变其他对象,而不知道具体有多少对象有待改变。(这里与Chain of Responsibility相似)
iii. 当一个对象必须通知其他对象,而她又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的。(两者之间的唯一关系,就是Observer必须实现update()方法)

d) 代码实例:
public class Subject{
private Vector observers;
public void addObserver(Observer observer){
observers.add(observer);
}
public void removeObserver(Observer observer){
observers.remove(observer);
}
public void notify(){
for(Observer o:observers){
o.update(this);
}
}
}

public class SubjectA extends Subject{
private int state;
public void stateChanged(){
super.notify();
}
}

public interface Observer{
public void update(Subject sub);
}

public class ObserverA implements Observer{
public void update(Subject sub){
doSomething;
}
}

e) 小结:
i. 目标和观察者之间的抽象耦合。目标只需要知道所有的观察者都实现了update()方法。
ii. 支持广播通信。目标与观察者之间是1-n的关系。
iii. 在Java中,由于Observerable是类,而Java不支持多继承,因此,当一个类如果她已继承了某个类,而又需要当一个Observerable时,可以使用一个内部类,让该内部类继承Observerable类。

6. State模式
a) 结构:



注: 1)允许一个对象在其内部状态改变时改变她的行为,对象看起来似乎修改了她的类。
2)Context通过替换她的state,达到修改其行为的目的。

b) 优点:
i. 把状态信息局部化到一个单独的类当中。(所有的State类)
ii. 她把状态变换外部化。(如果仅以一些内部数据值来定义当前状态时,状态改变就仅表示为一些变量赋值。但是,为不同的状态引入独立的对象,可以使得变换更加明确)
iii. 状态对象可以共享。(如果State对象没有实例变量,则该State对象可以共享,他们避让是没有内部状态,只有行为的轻量级对象)

c) 使用环境:
i. 一个对象的行为取决于她的状态,并且她必须在运行时刻根据状态改变她的行为。(类似于自动机模型,把行为与状态绑定到一起)
ii. 一个操作中含有庞大的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立化。

d) 代码实例:
public class Context{
private State state;
public void request(){
if(conditionA){
state = new StateA();
}else if(conditionB){
state = new StateB();
}
state.handle();
}
}

public interface State{
public void handle();
}

public class StateA implements State{
public void handle(){…}
}
public class StateB implements State{
public void handle(){…}
}
e) 小结:
i. 谁定义状态转换:如果转换规则是固定的,则可以由Context来定义。另一种方法是由每个State负责记录她的后继State,当发生转换时,由State设置Context的状态。

7. Strategy模式
a) 结构:



注: 1)定义一系列的算法,把他们一个个封装起来,并且使他们可相互替换,本模式使得算法可独立于使用他们的客户而变化。
2)Strategy模式与State模式非常相似,但是Strategy模式的目标是实现算法的替换,算法之间不存在顺序关系,State模式的目标是实现状态的转换,状态之间有一定的先后次序。

b) 优点:
i. 用户可以动态的选择策略。(替换strategy实例)
ii. 可以添加新的算法而不需修改客户代码。(所有的Strategy子类都有相同的接口)

c) 使用环境:
i. 许多相关的类仅仅是行为有异,“策略”提供了一种用多个行为中的一个来配置一个类的行为。
ii. 需要使用一个算法的不同变体。
iii. 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。
iv. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件移入他们各自的Strategy类中以代替这些语句。

d) 代码实例:
public class Context{
private Strategy strategy;
public void selectStrategy(String name){
if(name == conditionA)
strategy = new StrategyA();
else
strategy = new StrategyB();
}
public void request(){
strategy.handle();
}
}

public interface Strategy{
public void handle();
}

public class StrategyA implements Strategy{
public void handle(){…}
}

public class StrategyB implements Strategy{
public void handle(){…}
}

e) 小结:
i. Strategy类层次为Context定义了一系列可供重用的算法或行为。
ii. 将Context中使用的算法从Context中抽取出来,使得Context更加容易理解和重用。
iii. 消除了复杂的if-else语句。
iv. Strategy可以提供相同行为的不同实现。客户可以根据时间/空间权衡取舍,从不同的策略中进行选择。
v. 如果Strategy模式没有内部状态,则该实例可以在各Context间进行共享。
vi. 排序的算法,一般单独实现为一个Strategy,方便共享和重用。

8. Template模式
a) 结构:



注: 1)定义一个操作中的算法步骤,而将一些步骤的实现延迟到子类中。Template模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

b) 优点:
i. 对于公共方法可以在基类实现,而其他的可以在子类实现。

c) 使用环境:
i. 一次性实现一个算法的不变部分,并将可变部分留给子类来实现。(子类可重载或重写父类的方法)
ii. 各子类中公共的行为应该提取出来并集中到一个公共父类中以避免代码重复。
iii. 控制子类扩展,Template方法只在特定点调用hook操作,这样就只允许在这些点进行扩展。(在templateMethod中调用的方法,即为扩展点)

d) 代码实例:
public abstract class AbstractClass{
public void templateMethod(){
actionA();
actionB();
}
public abstract void actionA();
public abstract void actionB();
}

public class ConcreteClass extends AbstractClass{
public void actionA(){…}
public void actionB(){…}
}

e) 小结:
i. Template模式定义了一种方向的控制结构,与Framework的要求一致。

9. Visitor模式
a) 结构:



注: 1)表示一个作用于某对象结构中的元素的操作。她使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。(只要定义新的Visitor对象,然后遍历的visit每个element,即可定义新的操作)

b) 优点:
i. 在不同的对象间执行相同的操作。(不同的Element都具有相同的accept接口)
ii. 增加新的操作很方便。
iii. 访问者集中相关的操作而分离无关的操作。相关的操作都集中到Visitor中,而其他的负责维护列表或树形关系的操作,还是放在Element中。

c) 使用环境:
i. 一个对象的结构包含很多类对象。他们有不同的接口,而你想对这些对象实施一致的,依赖于具体类的操作。(在visit操作中,需要把Element传送到Visitor中,以使得Visitor能获取她所需要的,关于Element的信息)
ii. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作。而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中,当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。(特定的操作,都在visit方法中定义)
iii. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
d) 代码实例:
public interface Visitor{
public void visitA(Element e);
public void visitB(Element e);
}

public class VisitorA implements Visitor{
public void visitA(Element e){…}
public void visitB(Element e){…}
}

public class VisitorB implements Visitor{
public void visitA(Element e){…}
public void visitB(Element e){…}
}

public interface Element{
public void accept(Visitor v);
}

public class ElementA implements Element{
public void accept(Visitor v){
v.visitA(this);
}
}

public class ElementB implements Element{
public void accept(Visitor v){
v.visitB(this);
}
}

e) 小结:
i. 增加新的Element子类很困难。因为,这需要修改整个Visitor的结构,并添加新的visit方法。
ii. 破坏封装。因为,Visitor要成功执行他的visit方法时,需要了解Element的内部信息,这导致了Element需要提供一些方法,来暴露其内部细节。

10. 行为模式讨论
a) 封装变化:
i. 一个Stratergy对象封装一个算法。
ii. 一个State对象封装一个与状态相关的行为。
iii. 一个Mediator对象封装对象间的协议。
iv. 一个Iterator对象封装访问和遍历一个聚集对象中的各个构件的方法。
v. 一个Memento对象封装了对象的内部状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: