设计模式—访问者(Visitor)模式

from:http://blog.abreaking.com/c/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F_%E8%AE%BF%E9%97%AE%E8%80%85Visitor%E6%A8%A1%E5%BC%8F

Preface

最近在研究一个框架JSqlParser时,发现该框架使用了一种设计模式——访问者(Visitor)模式。遂在网上找了一下该设计模式相关知识,学习整理。

参考:http://www.cnblogs.com/idior/archive/2005/08/18/217500.html

概述

访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。

第一次这句话时,着实费解。关于对访问者模式的理解,可以先见下面这个例子,而后再看最后的综述。

举个例子

比如:你下午请客喝东西,那么就的考虑好你请的这些人中总会有口味不同,有些人喜欢喝茶,有些人喜欢喝果汁,有人喜欢喝咖啡。你当然只有把这些东西都准备好,尽量准备得全一些。那么来的客人喜欢喝啥就自己拿就是了。

那么我们定义一个基本的访问者操作的接口,反正你也不晓得要来的客人喜欢喝什么,那么就抽象一个Visitor什么都喝。

//基本访问着的操作
public interface Visitor {
    void visit(TeaWater teaWater);
    void visit(JuiceWater juiceWater);
    void visit(CaffeeWater caffeeWater);
}

这时,你的需要准备好要喝的东西来招待你的客人,定义一个方法的接口,不管是什么水,反正用来招待客人。

public interface Water {
    void accept(Visitor visitor);
}

 此外,你得把具体得水准备好了,准备好三样:茶、咖啡、果汁。不同类型的水也许会有各自的一些属性操作等等。

public class TeaWater implements Water {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getHotTea(){
        return "热茶";
    }
    public String getColdTea(){
        return "凉茶";
    }
}
public class CaffeeWater implements Water {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void addSugar(int number){
        System.out.print("添加了"+number+"勺糖");
    }

}
public class JuiceWater implements Water {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String getJuice(int size){
        return  size==0?"小杯":"大杯";
    }
}

我们再假设几个访问者实体类,假设zhangsan   lisi 就是你要邀请的两个人,他们的喜好定义如下:

public class ZhangsanVisitor implements  Visitor{

    @Override
    public void visit(TeaWater teaWater) {

        System.out.print("张三只喝茶,");
        String hotTea = teaWater.getHotTea();
        System.out.println("拿了"+hotTea);
    }

    @Override
    public void visit(JuiceWater juiceWater) {
        //不喜欢果汁,什么操作也不做
    }
    @Override
    public void visit(CaffeeWater caffeeWater) {
        //不喜欢咖啡,
    }

}
public class LisiVisitor implements Visitor {
    @Override
    public void visit(TeaWater teaWater) {
        System.out.print("能喝点茶");
        String coldTea = teaWater.getColdTea();
        System.out.println(",拿了"+coldTea);
    }

    @Override
    public void visit(JuiceWater juiceWater) {
        System.out.print("也能喝果汁");
        String juice = juiceWater.getJuice(1);
        System.out.println(",还是要"+juice);
    }

    @Override
    public void visit(CaffeeWater caffeeWater) {

        System.out.print("咖啡也要喝,");
        caffeeWater.addSugar(2);
    }
}

好了,接下来就是准备工作啦。你的把桌子准备好,用于放要喝的东西,招待客人。

public class Table {
    //准备好所有要喝的东西
    List waters = new ArrayList<>();

    public void addWater(Water water){
        waters.add(water);
    }
    //招待客人
    public void accept(Visitor visitor){
        for (Water water : waters){
            water.accept(visitor);
        }
    }
}

万事具备,客人来了,我们写个测试方法。准备好茶、咖啡、果汁来招待zhangsan、lisi。

@Test
    public void test01(){
        Table table = new Table();
        table.addWater(new TeaWater());//摆上茶
        table.addWater(new JuiceWater()); //摆上果汁
        table.addWater(new CaffeeWater()); //摆上咖啡


        Visitor zhangsan = new ZhangsanVisitor();
        Visitor lisi = new LisiVisitor();
        System.out.println("张三");
        table.accept(zhangsan);
        System.out.println("李四");
        table.accept(lisi);
    }

运行结果如下:

以上就是一个访问者模式的基本实例。

综述

关于访问者模式的UML图,可参见如下:

根据之前例子,可看到,对于访问者模式而言,两个最重要的角色:

  1. 访问者角色(Visitor):声明了一个或者多个访问操作,也就是说,该访问者需要做哪些操作;
  2. 节点角色(Element): 也就是Water这个接口的,它声明一个接受操作,接受一个访问者对象作为一个参量,也就是说,当访问者到来时,需要怎么做。

针对节点角色于访问者角色的方法可看到,节点角色接收一个访问者对象,并把自己作为变量再传给访问者对象,访问者对象再调用节点对象里的逻辑方法,这种操作过程被称为“双重分派”。

此外,对于Table这个对象,就是访问者模式的结构对象角色(ObjectStructure),它就是用于遍历节点中的所有元素。

可以看出,访问者模式适合于操作相对稳定的结构,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。也就是说,对于一个Visitor,它需要有什么操作,必须要非常清楚。如之前的例子,我招待客人,那么我很清楚,我只有茶、咖啡、果汁这个材料,我也只能拿这三个来招待客人。所以在Visitor方法中,就只有那三个方法。这是访问者模式最重要的一点,也是最有缺陷的一点。 如上个例子:有个人还想喝酒,我还得新增加一个节点类,及Water实现类,那么我还得在Visitor中加入一个方法,改动所有的Visitor的实现类,很明显,这样的效率是非常低下的。

 但是对于增加新的访问者那就非常的简单,如上的例子,无非就是多来一个客人,也就是新增加一个新Visitor实现类。

所以,访问者适用于具体操作能够非常稳定的情景下,能够很容易的拓展访问者(Visitor)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值