《一天一模式》— 訪問者模式

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方法。

以上說的能夠理解爲實現訪問者模式的招式,可是不要忘記,訪問者模式的心法是【將數據結構與操做方法分離】。

先理解心法,在學習招式,就能瓜熟蒂落的學會這個模式,這是我學習訪問者模式的一個經驗。

以上就是我對訪問者模式的一些理解,有不足之處請你們指出,謝謝。

相關文章
相關標籤/搜索