设计模式备忘 - 行为型

责任链模式(Chain of Responsibility)

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。从定义上可以看出,责任链模式的提出是为了“解耦”,以应变系统需求的变更和不明确性。

 

该模式又包含两种处理思想。纯的责任链模式,规定一个具体处理者角色只能对请求作出两种动作:自己处理;传给

下家。不能出现处理了一部分,把剩下的传给了下家的情况。而且请求在责任链中必须被处理,而不能出现无果而终的结局。反之,则就是不纯的责任链模式。具体由两个角色组成:

  • 抽象处理者角色(Handler):它定义了一个处理请求的接口。当然对于链子的不同实现,也可以在这个角色中实现后继链。
  • 具体处理者角色(Concrete Handler):实现抽象角色中定义的接口,并处理它所负责的请求。如果不能处理则访问它的后继者。

适用范围

  1. 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  2. 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可处理一个请求的对象集合应被动态指定。

一个较佳的例子就是Java的例外处理机制,当程式中发生例外时,也比会catch所捕捉的例外是否符合,如果符合就执行所设定的处理,如果都没有比对到适当的例外物件,就会将例外丢出try...catch区块之外。另一简单例子如下:

//抽象处理者角色
public class Handler { 
    private Handler successor;

    public void setSuccessor(Handler successor) { 
        this.successor = successor; 
    }

    public Handler getSuccessor() { 
        return successor; 
    }

    public void handleRequest(char c) { 
        if (successor != null) 
            successor.handleRequest(c); 
    }
}  

//具体处理者角色
public class SymbolHandler extends Handler { 
    public void handleRequest(char c) { 
        System.out.println("Symbol has been handled"); 
    } 
}

public class CharacterHandler extends Handler { 
    public void handleRequest(char c) { 
        if (Character.isLetter(c)) { 
            System.out.println("Character has been handled"); 
        } else {
            getSuccessor().handleRequest(c); 
        }
    }
}

public class NumberHandler extends Handler {
    public void handleRequest(char c) { 
        if (Character.isDigit(c)) { 
            System.out.println("Number has been handled"); 
        } else {
            getSuccessor().handleRequest(c); 
        }
    }
}

//具体使用
public class Application {
    public static void main(String[] args) throws IOException { 
        Handler numberHandler = new NumberHandler(); 
        Handler characterHandler = new CharacterHandler(); 
        Handler symbolHandler = new SymbolHandler(); 

        numberHandler.setSuccessor(characterHandler); 
        characterHandler.setSuccessor(symbolHandler); 

        System.out.print("Press any key then return: "); 
        char c = (char)System.in.read(); 
        numberHandler.handleRequest(c); 
    }
}

 

不足:责任链模式优点,上面已经体现出来了。无非就是降低了耦合、提高了灵活性。但是责任链模式可能会带来一些额外的性能损耗,因为它每次执行请求都要从链子开头开始遍历。

 

命令模式(Command)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。该模式由以下角色组成:

  • 命令角色(Command):声明执行操作的接口。由Java接口或者抽象类来实现。
  • 具体命令角色(Concrete Command):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
  • 客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。
  • 请求者角色(Invoker):调用命令对象执行这个请求。
  • 接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。

这个组成比较复杂,下面看一个简单的例子:

//请求者角色
public class Invoker {
    private Map commands;
    
    public Invoker() {
        commands = new HashMap();
    }
    
    public void addCommand(String commName, ICommand command) {
        commands.put(commName, command);
    }
    
    public void request(String commName) {
        ICommand command = (ICommand) commands.get(commName);
        command.execute();
    }
}

//命令角色
public interface ICommand {
    public void execute();
}

//接收者角色
public class UpperCaseHello implements ICommand {
    private String name;
    
    public UpperCaseHello(String name) {
        this.name = name;    
    }
    
    public void execute() {
        System.out.println("HELLO, " + name.toUpperCase());
    }
}

public class LowerCaseHello implements ICommand {
    private String name;
    
    public LowerCaseHello(String name) {
        this.name = name;    
    }
    
    public void execute() {
        System.out.println("hello, " + name.toLowerCase());
    }
}

//具体命令角色和客户角色
public class Client {
    public static void main(String[] args) {
        Invoker invoker = new Invoker();
        invoker.addCommand("JUSTIN", new UpperCaseHello("Justin"));
        invoker.addCommand("momor", new LowerCaseHello("momor"));
        
        // simulate random request
        String[] req = {"JUSTIN", "momor"};
        while (true) {
            int i = (int) (Math.random() * 10) % 2;
            invoker.request(req[i]);
        }
    }
}

 

该模式有待进一步研究。

 

迭代器模式(Iterator)

迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF 给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。该模式由以下角色组成:

  • 迭代器角色(Iterator):负责定义访问和遍历元素的接口。
  • 具体迭代器角色(Concrete Iterator):实现迭代器接口,并要记录遍历中的当前位置。
  • 容器角色(Container):负责提供创建具体迭代器角色的接口。
  • 具体容器角色(Concrete Container):实现创建具体迭代器角色的接口。
我们来看下Java Collection中的迭代器是怎么实现的吧。
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}
 
容器角色,这里以List为例。它也仅仅是一个接口,具体容器角色,便是实现了List接口的ArrayList等类。具体迭代器角色,它是以内部类的形式出来的。AbstractList是为了将各个具体容器角色的公共部分提取出来而存在的。
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    //create concrete iterator
    public Iterator<E> iterator() {
        return new Itr();
    }

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
}
 
适用情况:
  1. 支持以不同的方式遍历一个容器角色。根据实现方式的不同,效果上会有差别。
  2. 简化了容器的接口。但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。
  3. 对同一个容器对象,可以同时进行多个遍历。因为遍历状态是保存在每一个迭代器对象中的。
在实现自己的迭代器的时候,一般要操作的容器有支持的接口才可以。而且我们还要注意以下问题:
  1. 在迭代器遍历的过程中,通过该迭代器进行容器元素的增减操作是否安全呢?
  2. 在容器中存在复合对象的情况,迭代器怎样才能支持深层遍历和多种遍历呢?

中介者模式(Mediator):

GOF 给中介者模式下的定义是:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。简单点来说,将原来两个直接引用或者依赖的对象拆开,在中间加入一个“中介”对象,使得两头的对象分别和“中介”对象引用或者依赖。该模式的组成部分:

  • 抽象中介者(Mediator)角色:定义统一的接口用于各同事角色之间的通信。
  • 具体中介者(Concrete Mediator)角色:通过协调各同事角色实现协作行为,为此它要知道并引用各个同事角色。
  • 同事(Colleague)角色:每一个同事角色都知道对应的具体中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。

在结构上,中介者模式与观察者模式、命令模式都添加了中间对象——只是中介者去掉了后两者在行为上的方向。因此中介者的应用可以仿照后两者的例子去写。但是观察者模式、命令模式中的观察者、命令都是被客户所知的,具体哪个观察者、命令的应用都是由客户来指定的。而大多中介者角色对于客户程序却是透明的。当然造成这种区别的原因是由于它们要达到的目的不同。

 

从目的上看,调停者模式与观察者模式、命令模式便没有了任何关系,倒是与门面模式有些相似。但是门面模式是介于客户程序与子系统之间的,而中介者模式是介于子系统与子系统之间的。这也注定了它们有很大的区别:门面模式是将原有的复杂逻辑提取到一个统一的接口,简化客户对逻辑的使用。它是被客户所感知的,而原有的复杂逻辑则被隐藏了起来。而中介

者模式的加入并没有改变客户原有的使用习惯,它是隐藏在原有逻辑后面的,使得代码逻辑更加清晰可用。

 

备忘录模式(Memento)

备忘录(Memento)模式又称标记(Token)模式。GOF给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

 

其实单就实现保存一个对象在某一时刻的状态的功能,还是很简单的——将对象中要保存的属性放到一个专门管理备份的对象中,需要的时候则调用约定好的方法将备份的属性放回到原来的对象中去。但是你要好好看看为了能让你的备份对象访问到原对象中的属性,是否意味着你就要全部公开或者包内公开对象原本私有的属性呢?如果你的做法已经破坏了封装,那么就要考虑重构一下了。

 

下面是备忘录模式的组成部分:

  • 备忘录(Memento)角色:存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录,备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。
  • 备忘发起(Originator)角色:该角色创建一个备忘录,用以记录当前时刻它的内部状态,在需要时使用备忘录恢复内部状态。
  • 备忘录管理者(Caretaker)角色:负责保存好备忘录,不能对备忘录的内容进行操作或检查。

下面对三种在 Java 中可保存封装的方法进行探讨。


第一种就是采用两个不同的接口类来限制访问权限。这两个接口类中,一个提供比较完备的操作状态的方法,我们称它为宽接口,而另一个则可以只是一个标示,我们称它为窄接口。备忘录角色要实现这两个接口类。这样对于“备忘发起角色”采用宽接口进行访问,而对于其他的角色或者对象则采用窄接口进行访问。这种实现比较简单,但是需要人为的进行规范约束——而这往往是没有力度的。

第二种方法便很好的解决了第一种的缺陷:采用内部类来控制访问权限。将备忘录角色作为“备忘发起角色”的一个私有内部类。下面是一个例子:
class Originator {
    //这个是要保存的状态
    private int state= 90;
    //保持一个“备忘录管理者角色”的对象
    private Caretaker c = new Caretaker();

    //读取备忘录角色以恢复以前的状态
    public void setMemento(){
        Memento memento = (Memento) c.getMemento();
        state = memento.getState();
        System.out.println("the state is "+state+" now");
    }

    //创建一个备忘录角色,并将当前状态属性存入,托给“备忘录管理者角色”存放。
    public void createMemento(){
        c.saveMemento(new Memento(state));
    }

    //作为私有内部类的备忘录角色,它实现了窄接口,可以看到在第二种方法中宽接口已经不再需要。注意:里面的属性和方法都是私有的
    private class Memento implements MementoIF {
        private int state ;

        private Memento(int state){
            this.state = state ;
        }

        private int getState(){
            return state;
        }
    }
}

//窄接口
interface MementoIF {}

//备忘录管理者角色
class Caretaker {
    private MementoIF m ;
    
    public void saveMemento(MementoIF m) {
        this.m = m;
    }

    public MementoIF getMemento() {
        return m;
    }
}
 
第三种方式是不太推荐使用的:使用clone方法来简化备忘录模式。由于Java提供了clone机制,这使得复制一个对象变得轻松起来。使用了clone机制的备忘录模式,备忘录角色基本可以省略了,而且可以很好的保持对象的封装。但是在为你的类实现clone方法时一定要慎重。

GOF 在《设计模式》中总结了使用备忘录模式的前提:
  1. 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
  2. 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

观察者模式(Observer)

观察者(Observer)模式又名发布-订阅(Publish/Subscribe)模式。GOF 给观察者模式如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

 

该模式由以下部分组成:

  • 抽象目标角色(Subject):目标角色知道它的观察者,可以有任意多个观察者观察同一个目标。并且提供注册和删除观察者对象的接口。目标角色往往由抽象类或者接口来实现。
  • 抽象观察者角色(Observer):为那些在目标发生改变时需要获得通知的对象定义一个更新接口。抽象观察者角色主要由抽象类或者接口来实现。
  • 具体目标角色(Concrete Subject):将有关状态存入各个Concrete Observer对象。当它的状态发生改变时,向它的各个观察者发出通知。
  • 具体观察者角色(Concrete Observer):存储有关状态,这些状态应与目标的状态保持一致。实现Observer的更新接口以使自身状态与目标的状态保持一致。在本角色内也可以维护一个指向Concrete Subject对象的引用。

观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本:

  1. 一种情况便是目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式 - 就是说变化的信息是观察者角色主动从目标角色中“拉”出来的。
  2. 还有一种方法,那就是目标角色“服务一条龙”,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是“推模式” - 管你要不要,先给你。这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适。

适用范围:

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

 

策略模式(Strategy)

策略模式(Strategy)主要是定义一系列的算法,把这些算法一个个封装成拥有共同接口的单独的类,并且使它们之间可以互换。策略模式使这些算法在客户端调用它们的时候能够互不影响的变化。这里的算法不要狭义的理解为数据结构中的算法,可以理解为不同的业务处理方法。

 

先由定义来想象下它的结构吧。要使算法拥有共同的接口,这样就要实现一个接口或者一个抽象类出来才行:

  • 算法使用环境(Context)角色:算法被引用到这里和一些其它的与环境有关的操作一起来完成任务。
  • 抽象策略(Strategy)角色:规定了所有具体策略角色所需的接口。在Java它通常由接口或者抽象类来实现。
  • 具体策略(Concrete Strategy)角色:实现了抽象策略角色定义的接口。
使用建议:
  • 系统需要能够在几种算法中快速的切换。
  • 系统中有一些类它们仅行为不同时,可以考虑采用策略模式来进行重构。
  • 系统中存在多重条件选择语句时,可以考虑采用策略模式来重构。
  • 但是要注意一点,策略模式中不可以同时使用多于一个的算法。

状态模式(State)

GOF《设计模式》中给状态模式下的定义为:允许一个对象在其内部状态改变时改变它的行为。

 

能够让程序根据不同的外部情况来做出不同的响应,最直接的方法就是在程序中将这些可能发生的外部情况全部考虑到,使用if else语句来进行代码响应选择。但是这种方法对于复杂一点的状态判断,就会显得杂乱无章,容易产生错误。而且增加一个新的状态将会带来大量的修改。这个时候“能够修改自身”的状态模式的引入也许是个不错的主意。

 

由简单的开始会比较好理解状态模式的作用,先来看一个例子,如果您有一个只能顺时针转动的瓦斯开关,转动一次的状态为off、 small fire、medium fire与large fire,您如何在程序中控制状态的变化与行为呢?一个最简单的方式就是用if..else或是switch流程来控制,例如:

 

public class State { 
    private int state;

    public State() { 
        state = 0; 
    } 

    public void switchFire() { 
        if (state == 0) { 
            state = 1; 
            System.out.println( "small fire" ); 
        } else if (state == 1) { 
            state = 2; 
            System.out.println( "medium fire" ); 
        } else if (state == 2) { 
            state = 3; 
            System.out.println( "large fire" ); 
        } else { 
            state = 0; 
            System.out.println( "turning off" ); 
        } 
    } 
} 

 

这个方法很简单,每个人都会,但如果您的状态变化并不是流水式的变化,而是像TCP连线状态一样,会是一个网络图的时候,用 if...else或switch来写的话,您的程序就会乱的不像话了;来考虑如何让物件控制自己的状态转换与所应表现的行为,这个程序可以这样改写:

 

public interface IState { 
    public void switchFire(FireSwitch sw); 
}

public class OffState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new SmallState()); 
        System.out.println( "small fire" ); 
    } 
}

public class SmallState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new MediumState()); 
        System.out.println( "medium fire" ); 
    } 
}

public class MediumState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new LargeState()); 
        System.out.println( "large fire" ); 
    } 
}

public class LargeState implements IState { 
    public void switchFire(FireSwitch sw) { 
        sw.setState(new OffState()); 
        System.out.println( "off fire" ); 
    } 
}

public class FireSwitch { 
    private State current;

    public FireSwitch() { 
        current = new OffState(); 
    }

    public void setState(State s) { 
        current = s; 
    }

    public void switchFire() { 
        current.switchFire(this); 
    }
}

 

适用情况:

  • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。

状态 VS 策略:


Brandon Goldfedder 在《模式的乐趣》里是这么说的:“Strategy模式在结构上与State模式非常相似,但是在概念上,他们的目的差异非常大。区分这两个模式的关键是看行为是由状态驱动还是由一组算法驱动,这条规则似乎有点随意,但是在判断时还是需要考虑它。通常,State模式的“状态”是在对象内部的,Strategy模式的“策略”可以在对象外部,不过这也不是一条严格、可靠的规则。”

策略模式中,算法是否变化完全是由客户程序决定的,而且往往一次只能选择一种算法,不存在算法中途发生变化的情况。而状态模式如定义中所言,在它的生命周期中存在着状态的转变和行为得更改,而且状态变化是一个线形的整体,对于客户程序来言,这种状态变化往往是透明的。

模板模式(Template Method)

模板方法(Template Method)模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。可以看出来,模板方法模式也是为了巧妙解决变化对系统带来的影响而设计的。使用模板方法使系统扩展性增强,最小化了变化对系统的影响。

这个模式中仅仅使用到了继承关系,父类中定义一些抽象方法,再有子类去实现它们。

适用情况:
  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
  • 控制子类扩展,模板方法只在特定点调用操作,这样就只允许在这些点进行扩展。

访问者模式(Visitor)

《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于Java中的Collection概念了。

以下是访问者模式的组成结构:
  • 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
  • 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。
  • 元素角色(Element):定义一个Accept操作,它以一个访问者为参数。
  • 具体元素角色(Concrete Element):实现由元素角色提供的 Accept 操作。
  • 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
假设有一个集合,里面不仅储存一种类型的元素,如果想要对这些元素作出一些个别化的操作,首要条件就是要知道该元素的类型,使用instanceof似乎是个不错的方式,在程序简单的情况下,我们可以这么作:
public class ElementA { 
    // some implementing 
 } 
 
 public class ElementB { 
    // some implementing 
 } 

 public class ElementC { 
    // some implementing 
 } 

 // ...... 

Iterator iterator = arrayList.iterator() 
while (iterator.hasNext()) { 
    if (o instanceof ElementA) 
        (ElementA) o.operationA(); 
    else if (o instanceof ElementB) 
        (ElementB) o.operationB(); 
    else if (o instanceof ElementC) 
        (ElementC) o.operationC(); 
    else 
        System.out.println("Sorry! I don't know who you are! "  + o.toString()); 
    }

//....
 
这么作并不是不可以,只是将来的扩充性不大,如果想要一次改变对每一种类型元素的操作,必须修改很多地方。从元素自身的角度来想好了,元素在一个个的房子中,元素说:“不要在房子外费尽心思判断了,即然您不知道我是谁,那么您就进来访问我好了,我告诉您我是谁,这么一来您就知道如何操作我了!”于是我们用访问者模式来重构一下上面的代码:
public interface IElement { 
    public void accept(IVisitor visitor); 
} 

public class ElementA implements IElement { 
    public void accept(IVisitor visitor) { 
        visitor.visit(this); 
    }

    public void operationA() { 
        System.out.println("do A's job...."); 
    } 
} 

public class ElementB implements IElement { 
    public void accept(IVisitor visitor) { 
        visitor.visit(this); 
    }

    public void operationB() { 
        System.out.println("do B's job...."); 
    }
} 

public class ElementC implements IElement { 
    public void accept(IVisitor visitor) { 
        visitor.visit(this); 
    }

    public void operationC() { 
        System.out.println("do C's job...."); 
    } 
} 

public interface IVisitor { 
    public void visit(ElementA element); 
    public void visit(ElementB element); 
    public void visit(ElementC element); 
}  

public class VisitorA implements IVisitor { 
    public void visit(ElementA element) { 
        element.operationA(); 
    }

    public void visit(ElementB element) { 
        element.operationB(); 
    }

    public void visit(ElementC element) { 
        element.operationC(); 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
        // know nothing about their type 
        // after storing them into Element array 
        IElement[] list = {new ElementA(), new ElementB(), new ElementC()}; 
        IVisitor visitor = new VisitorA();
        for (int i=0; i < list.length; i++)  list[i].accept(visitor); 
    } 
} 
 
双重分派:

你在上面的例子中体会到双重分派的实现了没有?

首先在客户程序中将具体访问者模式作为参数传递给具体元素角色,这便完成了一次分派。进入具体元素角色后,具体元素角色调用作为参数的具体访问者模式中的visitor方法,同时将自己(this)作为参数传递进去。具体访问者模式再根据参数的不同来选择方法来执行。这便完成了第二次分派。

适用情况:
  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。
  • 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值