軟件設計模式學習(二十七)訪問者模式


訪問者模式是一種較爲複雜的行爲型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素具備不一樣的類型,且不一樣的訪問者能夠對其進行不一樣的訪問操做java


模式動機

對於系統中某些對象,它們存儲在同一個集合中,且具備不一樣的類型。對於該集合中的對象,能夠接受一類稱爲訪問者的對象來訪問,不一樣的訪問者其訪問方式有所不一樣。設計模式

在 Java 等面嚮對象語言中都提供了大量用於存儲多個元素的集合對象,集合中存儲的對象有時候是同一類型,有時候不是同一類型,或許它們只是具備共同的父類。假如咱們要針對一個包含不一樣類型元素的集合採起某種操做,而操做細節根據元素類型不一樣而不一樣,就會出現大量類型判斷語句,增大代碼複雜度。this

實際使用時,對相同元素的對象也可能存在多種不一樣的操做方式,並且可能還須要增長新的操做,此時訪問者模式是一個值得考慮的解決方案。設計


模式定義

表示一個做用於某對象結構中的各元素的操做,它使咱們能夠在不改變各元素的類的前提下定義做用於這些元素的新操做。訪問者模式是一種對象行爲型模式。code

Represent an operation to be performedd on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.orm


模式分析

訪問者模式結構較爲複雜,首先看一張模式結構類圖對象

對象結構(ObjectStructure)是一個元素集合,存儲了不一樣類型的元素對象,以供不一樣訪問者訪問。訪問者模式包括兩個層次,一個是訪問者層次結構,另外一個是元素層次結構。blog

訪問者層次結構提供了抽象訪問者(Visitor)和具體訪問者(ConcreteVisitor)。抽象訪問者聲明瞭訪問元素對象的方法,一般爲每一種類型的元素對象都提供一個訪問方法,而具體訪問者能夠實現這些訪問方法。element

這些訪問方法的設計又有兩種,一種是直接在方法名中標明待訪問元素對象的類型,如 visitConcreteElementA(ConcreteElementA elementA),還有一種是統一取名爲 visit(),經過參數類型的不一樣來定義一系列重載方法。rem

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() 方法的參數,這種調用機制也稱「雙重分派」。正由於使用了雙重分派技術,使得增長新的訪問者無須修改現有類庫代碼,只需將新的訪問者對象傳入具體元素對象的 accept() 方法便可,程序運行時將回調在 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);
    }
}

若是須要修改訪問者類型,只或者增長新的類型的訪問者,只需修改配置文件便可,符合開閉原則。但若是要增長新的類型的具體元素類,則訪問者類須要爲其定義新的訪問方法,從這一點看又違背了開閉原則。


模式優缺點

訪問者模式的優勢:

  • 使得增長新的訪問操做變得容易,無須修改現有類庫的代碼
  • 將有關類對象的訪問行爲i集中到一個訪問者對象中,而不是分散到一個個元素類,類的職責更加清晰
  • 能夠跨過類的等級結構訪問不一樣等級結構的元素類
  • 用戶可以在不修改現有類層次結構的狀況下,定義該類層次結構的新操做

訪問者模式的缺點:

  • 增長新的元素類很困難
  • 訪問者模式要求訪問者對象訪問並調用每個元素對象的操做,破壞了封裝性
相關文章
相關標籤/搜索