訪問者模式
根據軟件設計的開閉原則 (對修改關閉,對擴展開放),我們怎麼樣實現這種需求呢?
●Vistor(抽象訪問者):抽象訪問者爲對象結構中每一個具體元素類ConcreteElement聲明一個訪問操作,從這個操作的名稱或參數類型可以清楚知道需要訪問的具體元素的類型,具體訪問者需要實現這些操作方法,定義對這些元素的訪問操作。
●ConcreteVisitor(具體訪問者):具體訪問者實現了每個由抽象訪問者聲明的操作,每一個操作用於訪問對象結構中一種類型的元素。
●Element(抽象元素):抽象元素一般是抽象類或者接口,它定義一個accept()方法,該方法通常以一個抽象訪問者作爲參數。【稍後將介紹爲什麼要這樣設計。】
●ConcreteElement(具體元素):具體元素實現了accept()方法,在accept()方法中調用訪問者的訪問方法以便完成對一個元素的操作。
● ObjectStructure(對象結構):對象結構是一個元素的集合,它用於存放元素對象,並且提供了遍歷其內部元素的方法。它可以結合組合模式來實現,也可以是一個簡單的集合對象,如一個List對象或一個Set對象。
案例說明:
比如有一個公園,有一到多個不同的組成部分;該公園存在多個訪問者:清潔工A負責打掃公園的A部分,清潔工B負責打掃公園的B部分,公園的管理者負責檢點各項事務是否完成,上級領導可以視察 公園等等。也就是說,對於同一個公園,不同的訪問者有不同的行爲操作,而且訪問者的種類也可能需要根據時間的推移而變化(行爲的擴展性)。
代碼編寫:
Vistor(抽象訪問者)
public interface Vistor {
public void visit (Park park);
public void visit (ParkA parkA);
public void visit (ParkB parkB);
}
ConcreteVisitor(具體訪問者)
/**
* @author 孫一鳴 on 2020/2/16
* 清潔工A 負責 ParkA 地區的衛生
*/
public class VisitorA implements Vistor {
@Override
public void visit(Park park) {
}
@Override
public void visit(ParkA parkA) {
System.out.println("清潔工A 負責 ParkA 地區的衛生");
}
@Override
public void visit(ParkB parkB) {
}
}
/**
* @author 孫一鳴 on 2020/2/16
* 清潔工B 負責 ParkB 地區的衛生
*/
public class VisitorB implements Vistor {
@Override
public void visit(Park park) {
}
@Override
public void visit(ParkA parkA) {
}
@Override
public void visit(ParkB parkB) {
System.out.println("清潔工B 負責 ParkB 地區的衛生 ");
}
}
/**
* @author 孫一鳴 on 2020/2/16
*/
public class VistorManager implements Vistor {
@Override
public void visit(Park park) {
System.out.println("管理員: 負責公園檢查");
}
@Override
public void visit(ParkA parkA) {
System.out.println("管理員: 負責公園A檢查");
}
@Override
public void visit(ParkB parkB) {
System.out.println("管理員: 負責公園B檢查");
}
}
Element(抽象元素)
//它定義一個accept()方法,該方法通常以一個抽象訪問者作爲參數。【稍後將介紹爲什麼要這樣設計。】
/**
* @author 孫一鳴 on 2020/2/16
*/
public interface ParkElement {
//用來接待訪問者
//它定義一個accept()方法,該方法通常以一個抽象訪問者作爲參數。【稍後將介紹爲什麼要這樣設計。】
public void accept (Vistor vistor);
}
ConcreteElement(具體元素)
/**
* 公園的A區域
*
* @author 孫一鳴 on 2020/2/16
*/
public class ParkA implements ParkElement {
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
}
/**
* 公園的B區域
*
* @author 孫一鳴 on 2020/2/16
*/
public class ParkB implements ParkElement {
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
}
在具體元素類ConcreteElementA的accept()方法中,通過調用Visitor類的visit()方法實現對元素的訪問,並以當前對象作爲visit()方法的參數。其具體執行過程如下:
(1) 調用具體元素類的accept(Visitor visitor)方法,並將Visitor子類對象作爲其參數;
(2) 在具體元素類accept(Visitor visitor)方法內部調用傳入的Visitor對象的visit()方法,如visit(ConcreteElementA elementA),將當前具體元素類對象(this)作爲參數,如visitor.visit(this);
(3) 執行Visitor對象的visit()方法,在其中還可以調用具體元素對象的業務方法。
這種調用機制也稱爲 “雙重分派”,正因爲使用了雙重分派機制,使得增加新的訪問者無須修改現有類庫代碼,只需將新的訪問者對象作爲參數傳入具體元素對象的accept()方法,程序運行時將回調在新增Visitor類中定義的visit()方法,從而增加新的元素訪問方式。
ObjectStructure(對象結構)
package 設計模式.訪問者模式.風行;
/**
* 訪問者模式中的結構體
*
* @author 孫一鳴 on 2020/2/16
*/
public class Park implements ParkElement{
private ParkA parkA;
private ParkB parkB;
public Park() {
this.parkA = new ParkA();
this.parkB = new ParkB();
}
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
parkA.accept(vistor);
parkB.accept(vistor);
}
}
雙重分派:
使用了雙重分派機制,使得增加新的訪問者無須修改現有類庫代碼,只需將新的訪問者對象作爲參數傳入具體元素對象的accept()方法,程序運行時將回調在新增Visitor類中定義的visit()方法,從而增加新的元素訪問方式。
新增加一個訪問者 ----公園老闆
公園老闆要去視察公園 公園A 公園B
公園 公園A 公園B 不用去改變
public void accept(Vistor vistor) {
vistor.visit(this);
}
如果要在系統中增加一種新的訪問者,無須修改源代碼,只要增加一個新的具體訪問者類即可,在該具體訪問者中封裝了新的操作元素對象的方法。從增加新的訪問者的角度來看,訪問者模式符合“開閉原則”
如果要在系統中增加一種新的具體元素,例如增加一種新的員工類型爲“退休人員”,由於原有系統並未提供相應的訪問接口(在抽象訪問者中沒有聲明任何訪問“退休人員”的方法),因此必須對原有系統進行修改,在原有的抽象訪問者類和具體訪問者類中增加相應的訪問方法。從增加新的元素的角度來看,訪問者模式違背了“開閉原則”。