JAVA-设计模式-访问者模式

后端 · 2020-11-17 ·

在现实生活中,商场内所有的商品可以想象成一个集合对象,顾客在商场购物时放在“购物车”中的商品,顾客关心商品的价格和用处,而收银员关心商品的价格和数量。换句话说,同样的元素,不同的对象处理方式不同。

这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,而且不用修改原来的程序代码与数据结构。

定义

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。

角色


访问者模式的主要角色如下:

  1. 抽象访问者角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作visit(),该操作中的参数类型标识了被访问的具体元素。
  2. 具体访问者角色:实现抽象访问者角色中声明的各个访问操作,确定访问一个元素该做什么。
  3. 抽象元素角色:声明一个包含接受操作accept()的结构,被接受的访问者对象作为accept()方法参数。
  4. 具体元素角色:实现抽象元素角色提供的accept()操作,其方法体通常是 visitor.visit(this),另外具体元素中可能还包含业务逻辑的相关操作。
  5. 对象结构角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由List/Set/Map等聚合类实现。

代码

package xin.mla.visitor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class VisitorTest {

    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        Visitor viA = new ConcreteVisitorA();
        Visitor viB = new ConcreteVisitorB();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        os.accept(viA);
        System.out.println("######################");
        os.accept(viB);
    }

}

// 抽象访问者
interface Visitor {
    void visit(ConcreteElementA el);

    void visit(ConcreteElementB el);
}

// 具体访问者A类
class ConcreteVisitorA implements Visitor {
    public void visit(ConcreteElementA el) {
        System.out.println("顾客-->" + el.operationA());
    }

    public void visit(ConcreteElementB el) {
        System.out.println("顾客-->" + el.operationB());
    }
}

// 具体访问者B类
class ConcreteVisitorB implements Visitor {
    public void visit(ConcreteElementA el) {
        System.out.println("收银员-->" + el.operationA());
    }

    public void visit(ConcreteElementB el) {
        System.out.println("收银员-->" + el.operationB());
    }
}

// 抽象元素类
interface Element {
    void accept(Visitor vi);
}

// 具体元素A类
class ConcreteElementA implements Element {
    public void accept(Visitor vi) {
        vi.visit(this);
    }

    public String operationA() {
        return "具体元素A的操作。";
    }
}

// 具体元素B类
class ConcreteElementB implements Element {
    public void accept(Visitor vi) {
        vi.visit(this);
    }

    public String operationB() {
        return "具体元素B的操作。";
    }
}

// 对象结构角色
class ObjectStructure {
    private List<Element> lists = new ArrayList<Element>();

    public void accept(Visitor vi) {
        Iterator<Element> i = lists.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(vi);
        }
    }

    public void add(Element el) {
        lists.add(el);
    }

    public void remove(Element el) {
        lists.remove(el);
    }
}

优点

  1. 扩展性好,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  2. 复用性好,可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  3. 灵活性好,访问者模式将数据结构与作用于数据结构的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则,访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能单一化。

缺点

  1. 破坏封装性,访问者模式中具体元素对访问者公布细节。
  2. 违反依赖倒置原则,访问者模式依赖具体类,而没有依赖抽象类。
  3. 增加新的元素类很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体的访问者类中增加相应的具体操作,违背了“开闭原则”。
%