设计模式之访问者模式

访问者模式(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时,我愿意和你到天荒地老

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值