《一天一模式》— 访问者模式

1、访问者模式的概念

封装一些做用于某种数据结构中的各元素的操做,它能够在不改变这个数据结构的前提下定义做用于这些元素的新的操做。 java

听懂了这句话就不用往下看了,说明你会了。数据结构

听不懂我以为也正常,若是用一句话能学会就没人看书了。像我这种笨人,都是学会了一个模式,而后往它的定义上套。函数

2、何时使用访问者模式

访问者模式,要作的是把数据结构操做这个数据结构的处理方法给分离开来。学习

能够这么理解,有一个树形的数据结构,结构内部有不少操做这个结构的方法,好比遍历树、复制树、搜索树等等。this

当这个数据结构实现了足够多的需求后,实现数据结构的方法(如添加、修改、删除节点)操做数据结构的方法(如遍历、复制、搜索等)混淆在一块儿,有可能会臃肿不堪,难以阅读和维护。spa

这时候,能够选择使用访问者模式,将数据结构与操做分离开来,下面来讲说具体应该怎么作。3d

3、怎么使用访问者模式

3.1 不使用访问者模式带来的弊端

不使用访问者模式带来的弊端是数据结构的代码臃肿不堪,难以阅读和维护。在这里就不在进行代码演示说明了,我以前作过一个JS的Tree控件,大约有3000行代码在一个tree.js,能够想一想一下这个代码文件是多么臃肿不堪。code

3.2 如何使用访问者模式分离数据结构与操做

先举一个例子,能够沿用组合模式中的车辆品牌与车系的树形结构,将代码修剪后,类图以下:component

类的说明以下:对象

序号 名称 说明
1 Visitor 访问者抽象类,定义了访问方法,能够访问品牌和车系
2 ListVisitor 遍历操做访问者,实现了车型树和车系树的遍历操做
3 CopyVisitor 拷贝操做访问者,实现了车型树和车系树的拷贝操做
4 Element 可接收访问的数据结构的接口类
5 Component 组合模式中的组件类
6 Brand 车辆品牌类
7 Series 车系类

其中Visitor,ListVisitor,CopyVisitor表明访问者类,他能够操做数据结构。

其他的Element,Component,Brand,Series表明数据结构类,他们能够接受一个访问者访问本身。

类图不太直观,下面直接使用代码来介绍访问者模式是如何使用的。

3.3 代码示例

/**
 * 访问者的抽象类,定义了访问数据结构中不一样类型的方法。
 * 采用方法重载的方式实现了能够操做多个类型。
 */
public abstract class Visitor {

    public abstract void visit(Brand brand);

    public abstract void visit(Series series);

}

/**
 * 遍历操做的访问者。
 * 继承访问者的抽象类,并实现了抽象方法。
 * 因为是遍历操做的访问者,方法中主要实现遍历业务。
 */
public class ListVisitor extends Visitor {
    public void visit(Brand brand) {
        System.out.println(brand.getName() + "遍历品牌树。");
    }

    public void visit(Series series) {
        System.out.println(series.getName() + "遍历车系树。");
    }
}

/**
 * 拷贝操做的访问者。
 * 继承访问者的抽象类,并实现了抽象方法。
 * 因为是拷贝操做的访问者,方法中主要实现拷贝业务。
 */
public class CopyVisitor extends Visitor {
    public void visit(Brand brand) {
        System.out.println(brand.getName() + "复制品牌树。");
    }

    public void visit(Series series) {
        System.out.println(series.getName() + "复制车系树。");
    }
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------

/**
 * 容许被访问者操做的数据结构,都须要实现该接口。
 *
 * 接口中有一个方法声明,接受一个访问者抽象类。
 */
public interface Element {

    void accept(Visitor visitor);

}

/**
 * 组件对象
 */
public abstract class Component implements Element {

    public String getName() {
        throw new UnsupportedOperationException();
    }

    public void add(Component component) {
        throw new UnsupportedOperationException();
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }

}

/**
 * 品牌
 */
public class Brand extends Component {

    private List<Component> series = new ArrayList<Component>();

    private String name;

    public Brand(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void add(Component component) {
        series.add(component);
    }

    public void remove(Component component) {
        series.remove(component);
    }

    /**
     * 关键方法!经过参数,接收一个访问者。
     * 而后调用访问者的方法,将自己经过参数传递给访问者。
     */
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}

/**
 * 车系
 */
public class Series extends Component {

    private String name;

    public Series(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    /**
     * 关键方法!经过参数,接收一个访问者。
     * 而后调用访问者的方法,将自己经过参数传递给访问者。
     */
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}

以上是访问者模式的实现,下面编写一个Main函数来使用这个模式:

public class Client {

    public static void main(String[] args) {
        // 建立数据结构
        Brand brand = new Brand("奔驰。");
        Series series1 = new Series("奔驰A系。");
        Series series2 = new Series("奔驰C系。");
        Series series3 = new Series("奔驰E系。");
        brand.add(series1);
        brand.add(series2);
        brand.add(series3);
        // 调用访问者操做数据结构。
        // 当调用数据结构对象的accept时候,传入一个访问者。
        // 等于在调用accept方式时,决定须要作什么操做。
        // 具体的操做封装到了访问者中,而数据结构会在accept里以参数的形式传送给访问者。
        brand.accept(new ListVisitor());
        brand.accept(new CopyVisitor());
        series1.accept(new ListVisitor());
        series1.accept(new CopyVisitor());
    }

}

4、总结

若是以为访问者模式很绕,能够主要看这几处:

public abstract class Visitor {

    public abstract void visit(Brand brand);

    public abstract void visit(Series series);

}

定义访问者时,visit方法的参数是数据结构

public interface Element {

    void accept(Visitor visitor);

}

定义数据结构时,必须实现这个结构,也就是说数据结构中都要实现一个accept方法,接收一个具体的访问者

public void accept(Visitor visitor) {
        visitor.visit(this);
    }

方法实现是在accept中调用访问者的visit方法,将数据结构自身(this)传给访问者的visit方法

series1.accept(new ListVisitor());

在要对数据结构作操做的时候,将须要的访问者传给accept方法。

以上说的能够理解为实现访问者模式的招式,可是不要忘记,访问者模式的心法是【将数据结构与操做方法分离】。

先理解心法,在学习招式,就能瓜熟蒂落的学会这个模式,这是我学习访问者模式的一个经验。

以上就是我对访问者模式的一些理解,有不足之处请你们指出,谢谢。

相关文章
相关标签/搜索