设计模式之访问者模式

来源:互联网 发布:林彪军事才能 知乎 编辑:程序博客网 时间:2024/05/19 05:29

访问者模式(Visitor Pattern):在不改变某对象结构中元素的前提下,定义作用于这些元素的新操作。

访问者模式适用于数据结构相对稳定的系统,可以把数据结构和作用于结构上的操作解耦,使操作集合可以相对自由地变化。一种操作就相当于一个访问者。访问者模式将有关操作行为集中到一个访问者对象中。

访问者模式的缺点在于会使数据结构的变化变得困难。

比如男人和女人对于不同的事件有不同的反应,如果单纯抽象出Person基类,Man和Woman根据传递进来的不同事件作出不同反应,这样新增事件的话都要修改Man和Woman,违反了封闭开放原则。

可以把变化的地方,也就是不同的事件,抽象一个基类,然后基于这个基类进行扩展,可以达到比较好的解耦效果。

实例实现

首先定义数据基类,基本方法就是对事件进行反应:

public interface Person {    void react(Event visitor);}

两个数据类,访问者模式一般应用于数据元素比较稳定的情况下:

class Man implements Person {    @Override    public void react(Event visitor) {        visitor.getManReaction(this);    }}class Woman implements Person {    @Override    public void react(Event visitor) {        visitor.getWomanReaction(this);    }}

此处用到一个技术叫双分派,就是在数据对象的一个方法中,接收事件对象作为参数,这是一次分派;然后接收到的事件对象调用自己的方法,传入调用它的数据对象作为方法参数,这就是第二次分派。双分派技术意味着方法的执行不仅取决于方法参数,还取决于执行方法的对象,由方法参数和调用者本身共同决定执行结果。

然后定义事件类,也就是模式里的访问者,事件类就是定义数据类各自的反应方法,如果数据元素稳定不变的话,事件类的方法也就不用去修改:

public interface Event {    void getManReaction(Man man);    void getWomanReaction(Woman woman);}

事件类的具体实现:

class Success implements Event {    @Override    public void getManReaction(Man man) {        System.out.println(man.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,背后一般有个伟大的女人");    }    @Override    public void getWomanReaction(Woman woman) {        System.out.println(woman.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,背后一般有个不成功的男人");    }}class Failure implements Event {    @Override    public void getManReaction(Man man) {        System.out.println(man.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,独自借酒浇愁");    }    @Override    public void getWomanReaction(Woman woman) {        System.out.println(woman.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,需要人来安慰");    }}class Marriage implements Event {    @Override    public void getManReaction(Man man) {        System.out.println(man.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,我会照顾你一辈子");    }    @Override    public void getWomanReaction(Woman woman) {        System.out.println(woman.getClass().getSimpleName() + " "                + this.getClass().getSimpleName() + "时,我愿意和你到天荒地老");    }}

然后我们还可以定义一个数据结构,用来组织数据元素对于不同访问者的反应结果:

public class DataStructure {    private List<Person> elements = new ArrayList<>();    public void addElement(Person element) {        elements.add(element);    }    public void removeElement(Person element) {        elements.remove(element);    }    public void showReaction(Event visitor) {        for (Person person : elements) {            person.react(visitor);        }    }}

测试类:

public class Main {    public static void main(String[] args) {        final DataStructure dataStructure = new DataStructure();        final Man man = new Man();        final Woman woman = new Woman();        dataStructure.addElement(man);        dataStructure.addElement(woman);        dataStructure.showReaction(new Success());        System.out.println();        dataStructure.showReaction(new Failure());        System.out.println();        dataStructure.showReaction(new Marriage());    }}

输出:

Man Success时,背后一般有个伟大的女人Woman Success时,背后一般有个不成功的男人Man Failure时,独自借酒浇愁Woman Failure时,需要人来安慰Man Marriage时,我会照顾你一辈子Woman Marriage时,我愿意和你到天荒地老

访问者模式使得数据元素不变的情况下,新增事件只要创建新的实现即可,不用修改原有代码,很好地符合了封闭开放原则。

原创粉丝点击