访问者模式



模式动机

对于系统中某些对象,它们存储在同一个集合中,且具有不同的类型。对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式有所不同

Java 提供了用于存储多个元素的集合对象,集合中存储的对象有时候是同一类型,有时候不是同一类型,或许它们只是具有共同的父类。假如我们要针对一个包含不同类型元素的集合采取某种操作,而操作细节根据元素类型不同而不同,就会出现大量类型判断语句,增大代码复杂度

实际使用时,对相同元素的对象也可能存在多种不同的操作方式,而且可能还需要增加新的操作,此时访问者模式是一个值得考虑的解决方案。访问者模式包含访问者和被访问元素两个主要组成部分,这些被访问的元素具有不同的类型,且不同的访问者可以对其进行不同的访问操作


模式分析

访问者模式结构较为复杂,首先看一张模式结构类图

对象结构(ObjectStructure)是一个元素集合,存储了不同类型的元素对象,以供不同访问者访问。访问者模式包括两个层次,一个是访问者层次结构,另一个是元素层次结构

访问者层次结构提供了抽象访问者(Visitor)和具体访问者(ConcreteVisitor)。抽象访问者声明了访问元素对象的方法,通常为每一种类型的元素对象都提供一个访问方法,而具体访问者可以实现这些访问方法

这些访问方法的设计又有两种,一种是直接在方法名中标明待访问元素对象的类型,如 visitConcreteElementA(ConcreteElementA elementA),还有一种是统一取名为 visit(),通过参数类型的不同来定义一系列重载方法。

public abstract class Visitor {
    // 统一取名
    public abstract void visit(ConcreteElementA elementA);
    public abstract void visit(ConcreteElementB elementB);
    // 如果所有访问者对某一类型的元素访问操作都相同
    // 则可以将操作代码移到抽象访问者中
    public void visit(ConcreteElementC elementC) {
        ...
    }
}

元素层次结构提供了抽象元素类(Element)和具体元素类(ConcreteElementA),抽象元素类一般都声明一个 accept() 方法,用于接受访问者的访问。该方法传入一个抽象访问者 Visitor 类型的参数,在程序运行时确定其具体访问者的类型,并调用具体访问者对象的 visit() 方法实现对元素对象的操作

public class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public void operationA() {
        // 在具体元素类中可以定义不同类型的元素所特有的业务方法
    }
}

具体元素类 ConcreteElementA 的 accept() 方法通过调用 Visitor 类的 visit() 方法实现对元素的访问,并以当前对象作为 visit() 方法的参数,程序运行时将回调在 Visitor 类中定义的 visit() 方法,从而实现不同形式的访问

对象结构(ObjectStructure)是一个集合,用于存储元素对象并接受访问者的访问。在对象结构中可以使用迭代器对存储在集合中的元素对象进行遍历,并逐个调用每一个对象的 accept() 方法,实现对元素对象的访问操作

public class ObjectStructure {
    
    private ArrayList list = new ArrayList();
    
    public void accept(Visitor visitor) {
        Iterator i = list.iterator();
        while(i.hashNext()) {
            ((Element)i.next()).accept(visitor);
        }
    }
    
    public void addElement(Element element) {
		list.add(element);
    }
    
    public void removeElement(Element element) {
        list.remove(element);
    }
}

最终在客户端我们需要实例化一个对象结构对象,并向其添加元素对象,再调用 accept() 方法来接受访问者对象的访问。具体访问者类型可以通过配置文件来确定。

public class Client {
    
    public static void main(String[] args) {
        Element elementA = new ElementA();
        Element elementB = new ElementB();
        Element elementC = new ElementC();
        
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addElement(elementA);
        objectStructure.addElement(elementB);
        objectStructure.addElement(elementC);
        
        Visitor visitor = new ConcreteVisitorA();
        objectStructure.accept(visitor);
    }
}
posted @ 2020-06-18 15:36  低吟不作语  阅读(461)  评论(0编辑  收藏  举报