设计模式(十八)访问者模式

版权声明:转载必须注明本文转自晓_晨的博客:http://blog.csdn.net/niunai112

目录

导航

设计模式之六大设计原则
设计模式(一)单例模式
设计模式(二)工厂模式
设计模式(三)策略模式
设计模式(四)适配器模式
设计模式(五)享元模式
设计模式(六)建造者模式
设计模式(七)原型模式
设计模式(八)桥接模式
设计模式(九)外观模式
设计模式(十)组合模式
设计模式(十一)装饰器模式
设计模式(十二)代理模式
设计模式(十三)迭代器模式
设计模式(十四)观察者模式
设计模式(十五)中介者模式
设计模式(十六)命令模式
设计模式(十七)状态模式
设计模式(十八)访问者模式
设计模式(十九)责任链模式
设计模式(二十)解释器模式
设计模式(二十一)备忘录模式
设计模式(二十二)模板模式
设计模式总结篇(为什么要学习设计模式,学习设计模式的好处)

前言

访问者模式是23个设计模式比较复杂的一个模式,在LZ看来应该是仅次于解释器模式的一种设计模式了。这个模式,将访问数据的过程分为,访问者,被访问物,结构整合类,三个对象。解耦了访问过程。当被访问物是恒定的,不需要改动的情况下,使用访问者模式比较有优势,否则不要使用访问者模式。
visitor

访问者(Visitor):接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

具体访问者(ConcreteVisitor):具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

被访问元素(Element):元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

具体被访问元素(ConcreteElementA):具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

结构类(ObjectStructure):定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

例子

一个抽象实现例子,想要理解例子,LZ还得插个嘴,给介绍一下分派的概念,
静态分派:就是按照变量的类型进行分派,确定究竟用哪个方法执行,静态分派在编译期确定方法的使用版本。
动态分派:动态分派在运行期确定方法使用的版本,就是说只有在运行时,根据实际类的类型来确定使用的方法。
这两个概念在下面的例子中都有体现。

/***
 *
 *@Author ChenjunWang
 *@Description:具体访问物接口
 *@Date: Created in 17:45 2018/4/12
 *@Modified By:
 *
 */
public interface Node {
    //这里是动态分派,接口类不好体现。我们看下结构类
    void accept(Visitor visitor);
}

/***
 *
 *@Author ChenjunWang
 *@Description:具体访问物类A
 *@Date: Created in 18:29 2018/4/12
 *@Modified By:
 *
 */
public class ConcreteNodeA implements Node{
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}


/***
 *
 *@Author ChenjunWang
 *@Description:具体访问物类B
 *@Date: Created in 18:29 2018/4/12
 *@Modified By:
 *
 */
public class ConcreteNodeB implements Node{
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);

    }
}
/***
 *
 *@Author ChenjunWang
 *@Description:具体访问者接口
 *@Date: Created in 17:31 2018/4/12
 *@Modified By:
 *
 */
public interface Visitor {
    //这里用到了静态分派,在编译期就能确定,ConcreteNodeA参数走visit(ConcreteNodeA concreteNodeA)方法,
    //ConcreteNodeB参数走visit(ConcreteNodeA concreteNodeB)方法。利用方法的重载
    void visit(ConcreteNodeA concreteNodeA);
    void visit(ConcreteNodeB concreteNodeB);
}

/***
 *
 *@Author ChenjunWang
 *@Description:具体访问者B
 *@Date: Created in 17:45 2018/4/12
 *@Modified By:
 *
 */
public class ConcreteVisitorA implements Visitor{
    @Override
    public void visit(ConcreteNodeA concreteNodeA) {


        //具体处理过程写这里面
        System.out.println("ConcreteVisitorA 处理 concreteNodeA");
    }

    @Override
    public void visit(ConcreteNodeB concreteNodeB) {
        //具体处理过程写这里面
        System.out.println("ConcreteVisitorA 处理 concreteNodeB");
    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:具体访问者B
 *@Date: Created in 17:45 2018/4/12
 *@Modified By:
 *
 */
public class ConcreteVisitorB implements Visitor{
    @Override
    public void visit(ConcreteNodeA concreteNodeA) {
        //具体处理过程写这里面
        System.out.println("ConcreteVisitorB 处理 concreteNodeA");
    }

    @Override
    public void visit(ConcreteNodeB concreteNodeB) {
        //具体处理过程写这里面
        System.out.println("ConcreteVisitorB 处理 concreteNodeB");
    }
}

/***
 *
 *@Author ChenjunWang
 *@Description:结构类
 *@Date: Created in 18:30 2018/4/12
 *@Modified By:
 *
 */
public class ObjectStructure {
    private List<Node> list = new ArrayList<>();
    public void action(Visitor visitor){
        for (Node n : list){
            //看完LZ的例子后再来看这里,你会知道visit是有2个方法的,一个参数是ConcreteNodeA,另一个是ConcreteNodeB。
            //在编译期,肯定不能确定这个Node是那个具体的类型,只有到运行期间,代码走到这,才知道具体的类型,
            // 假如是ConcreteNodeA类型,走visit(ConcreteNodeA concreteNodeA);假如是ConcreteNodeB类型,走visit(ConcreteNodeA concreteNodeB),
            // 所以这个是动态分派的地方。主要是利用多态特性
            n.accept(visitor);
        }
    }
    public void add(Node node){
        list.add(node);
    }
}


/***
 *
 *@Author ChenjunWang
 *@Description:测试类
 *@Date: Created in 18:30 2018/4/12
 *@Modified By:
 *
 */
public class Test {
    public static void main(String[] args) {

        ObjectStructure objectStructure = new ObjectStructure();
        ConcreteNodeA concreteNodeA = new ConcreteNodeA();
        ConcreteNodeB concreteNodeB = new ConcreteNodeB();
        objectStructure.add(concreteNodeA);
        objectStructure.add(concreteNodeB);
        System.out.println("------------------访问者A访问元素------------------");
        objectStructure.action(new ConcreteVisitorA());
        System.out.println("------------------访问者B访问元素------------------");
        objectStructure.action(new ConcreteVisitorB());
    }
}

运行结果如下
----------------------------------------------------
------------------访问者A访问元素------------------
ConcreteVisitorA 处理 concreteNodeA
ConcreteVisitorA 处理 concreteNodeB
------------------访问者B访问元素------------------
ConcreteVisitorB 处理 concreteNodeA
ConcreteVisitorB 处理 concreteNodeB

总结

优点

(1)解耦访问者与访问物的之间的关系。
(2)要增加访问者非常简单,只需要增加一个访问者类,实现对各个对访问物的操作方法即可。

缺点

(1)增加访问物非常麻烦,要修改所有的访问者,破坏了开闭原则。
(2)系统的复杂程度也会大大提升。

Git地址

本篇实例Github地址:https://github.com/stackisok/Design-Pattern/tree/master/src/visitor

回到最上方


有什么不懂或者不对的地方,欢迎留言。
喜欢LZ文章的小伙伴们,可以关注一波,也可以留言,LZ会回你们的。
觉得写得不错的小伙伴,欢迎转载,但请附上原文地址,谢谢^_^!

猜你喜欢

转载自blog.csdn.net/niunai112/article/details/79954975
今日推荐