訪問者——對象行爲模式

意圖

封裝某些作用於某種數據結構中各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。


動機

我們去銀行櫃檯辦業務,一般情況下會開幾個個人業務櫃檯的,你去其中任何一個櫃檯辦理都是可以的。我們的訪問者模式可以很好付諸在這個場景中:對於銀行櫃檯來說,他們是不用變化的,就是說今天和明天提供個人業務的櫃檯是不需要有變化的。而我們作爲訪問者,今天來銀行可能是取消費流水,明天來銀行可能是去辦理手機銀行業務,這些是我們訪問者的操作,一直是在變化的。


適用性

當系統中存在着固定的數據結構,而有着不同的行爲,那麼訪問者模式也許是個不錯的選擇。
假如一個對象中存在着一些與本對象不相干(或者關係較弱)的操作,爲了避免這些操作污染這個對象,則可以使用訪問者模式來把這些操作封裝到訪問者中去。


結構圖

這裏寫圖片描述

角色

抽象訪問者:抽象類或者接口,聲明訪問者可以訪問哪些元素,具體到程序中就是visit方法中的參數定義哪些對象是可以被訪問的。
訪問者:實現抽象訪問者所聲明的方法,它影響到訪問者訪問到一個類後該幹什麼,要做什麼事情。
抽象元素類:接口或者抽象類,聲明接受哪一類訪問者訪問,程序上是通過accept方法中的參數來定義的。抽象元素一般有兩類方法,一部分是本身的業務邏輯,另外就是允許接收哪類訪問者來訪問。
元素類:實現抽象元素類所聲明的accept方法,通常都是visitor.visit(this),基本上已經形成一種定式了。
結構對象:一個元素的容器,一般包含一個容納多個不同類、不同接口的容器,如List、Set、Map等,在項目中一般很少抽象出這個角色。


實現

abstract class Element {
    public abstract void accept(IVisitor visitor);
    public abstract void doSomething();
}

interface IVisitor {
    public void visit(ConcreteElement1 el1);
    public void visit(ConcreteElement2 el2);
}

class ConcreteElement1 extends Element {
    public void doSomething(){
        System.out.println("這是元素1");
    }

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

class ConcreteElement2 extends Element {
    public void doSomething(){
        System.out.println("這是元素2");
    }

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

    public void visit(ConcreteElement1 el1) {
        el1.doSomething();
    }

    public void visit(ConcreteElement2 el2) {
        el2.doSomething();
    }
}

class ObjectStruture {
    public static List<Element> getList(){
        List<Element> list = new ArrayList<Element>();
        Random ran = new Random();
        for(int i=0; i<10; i++){
            int a = ran.nextInt(100);
            if(a>50){
                list.add(new ConcreteElement1());
            }else{
                list.add(new ConcreteElement2());
            }
        }
        return list;
    }
}

public class Client {
    public static void main(String[] args){
        List<Element> list = ObjectStruture.getList();
        for(Element e: list){
            e.accept(new Visitor());
        }
    }
}

優缺點

優點

1,訪問者模式使得增加新的操作變得很容易。如果一些操作依賴於一個複雜的結構對象的話,那麼一般而言,增加新的操作會很複雜。而使用訪問者模式,增加新的操作就意味着增加一個新的訪問者類,因此,變得很容易。
2,訪問者模式將有關的行爲集中到一個訪問者對象中,而不是分散到一個個的節點類中。
3,訪問者模式可以跨過幾個類的等級結構訪問屬於不同的等級結構的成員類。迭代子只能訪問屬於同一個類型等級結構的成員對象,而不能訪問屬於不同等級結構的對象。訪問者模式可以做到這一點。
4,積累狀態。每一個單獨的訪問者對象都集中了相關的行爲,從而也就可以在訪問的過程中將執行操作的狀態積累在自己內部,而不是分散到很多的節點對象中。這是有益於系統維護的優點。

缺點

1,增加新的節點類變得很困難。每增加一個新的節點都意味着要在抽象訪問者角色中增加一個新的抽象操作,並在每一個具體訪問者類中增加相應的具體操作。
2,破壞封裝。訪問者模式要求訪問者對象訪問並調用每一個節點對象的操作,這隱含了一個對所有節點對象的要求:它們必須暴露一些自己的操作和內部狀態。不然,訪問者的訪問就變得沒有意義。由於訪問者對象自己會積累訪問操作所需的狀態,從而使這些狀態不再存儲在節點對象中,這也是破壞封裝的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章