java設計模式——訪問者

在訪問者(Visitor)模式中,客戶通過一個訪問者類,將對於一個數據結構的不同元素的訪問方法封裝起來,這樣,對一類元素的訪問方法可以通過在訪問者中修改邏輯而實現,這種類型的設計模式屬於行為型模式。

如果對一個數據結構的訪問欲獲得的結果,需要該結構內部屬性經過一系列計算才能得到,那麼一般情況下,我們會將計算過程寫到數據結構中,用一個統一的接口約束數據結構的計算方法,保證都能獲得一致的結構。但是如果有新的計算功能需求,就要對數據結構類進行修改,顯然,這不符合開閉原則。所以我們可以將對不同數據結構中的屬性的計算方法抽象到一個訪問者類中。這樣,當需要修改算法的時候只需修改訪問者中的具體邏輯就可以了,一定程度上將數據結構和算法分離開來了。

實例

現在假設公司的領導要到各個部門視察,領導每次視察的時候都可能提出不同的問題,需要部門的主管以及員工等做不同的整改工作。我們將部門抽象成 Unit 類,將領導抽象成 Leader 類,每次領導視察過後,部門中都要對工作方式和計劃做一些調整,這時就要修改 Unit 類了,使新的類能夠對領導新提出的需求做出合理的響應。如果領導每次來都會在不同的方面提出意見,那麼就要頻繁的對 Unit 類進行修改。為了避免這些繁瑣的修改工作,我們可以用一個新的 UnitVisitor 類來替代 Leader 類。 UnitVisitor 類中封裝了對不同部門的訪問方法,這時我們將 Unit 類進一步抽象,抽象出一個獨立的接口,接口裡包含被 UnitVisitor 類訪問的方法。

Unit.java


public abstract class Unit {
// 子部門
private Unit[] children;
// 由於子部門可以有多個,也可以沒有,所以這裡使用可變長參數
public Unit(Unit... children) {
this.children = children;
}
/**
* 接受訪問
*/
public void beVisited(UnitVisitor visitor) {
for (Unit childUnit : children) {
childUnit.beVisited(visitor);
}
}
}

一個部門可以有若干個子部門,訪問者訪問某個部門的時候也會同時訪問其所有的子部門,這樣一直遞歸訪問下去,直到訪問的子部門沒有下屬部門為止。

現在出現了三種被訪問對象,他們分別是 Boss 主管, Enginner 工程師和 Mangager 經理,這三種角色之間存在直接的領導和被領導的關係。訪問者對三種角色進行訪問時獲得的響應也是不同的。

Boss.java


public class Boss extends Unit {
public Boss(Unit... children) {
super(children);
}

@Override
public void beVisited(UnitVisitor visitor) {
visitor.visitBoss(this);
super.beVisited(visitor);
}
@Override
public String toString() {
return "老闆";
}
}

Mangager.java


public class Manager extends Unit {

public Manager(Unit... children) {
super(children);
}
@Override
public void beVisited(UnitVisitor visitor) {
visitor.visitManager(this);
super.beVisited(visitor);
}
@Override
public String toString() {
return "經理";
}
}

Engineer.java


public class Engineer extends Unit {

public Engineer(Unit... children) {
super(children);
}
@Override
public void beVisited(UnitVisitor visitor) {
visitor.visitEngineer(this);
super.beVisited(visitor);
}

@Override
public String toString() {
return "工程師";
}

}

不難看出,每個角色的訪問方法中都調用了作為參數的訪問者的某個訪問方法,這樣寫的好處是可以將訪問方法和具體的被訪問者解耦,至於如何被訪問以及該提供哪些內容,被訪問者其實並不不需要關心。

接下來就要寫訪問者接口了,這個接口中囊括了適配所有不同被訪問者類的訪問方法,接口的實現類只需根據自身的需要重寫某個或某幾個方法即可。

UnitVisitor.java


public interface UnitVisitor {

void visitEngineer(Engineer engineer);

void visitBoss(Boss boss);

void visitManager(Manager manager);
}

關於三種角色:主管、經理和工程師,也分別有不同的訪問者,他們分別是 BossVisitor、 ManagerVisitor 和 EngineerVisitor,它們都實現 Visitor 接口。

BossVisitor.java


public class BossVisitor implements UnitVisitor {

private static final Logger LOGGER = LoggerFactory.getLogger(BossVisitor.class);
@Override
public void visitEngineer(Engineer engineer) {


}
@Override
public void visitBoss(Boss boss) {
LOGGER.info("你好,{}", boss);
}
@Override
public void visitManager(Manager manager) {
}
}

ManagerVisitor.java


public class ManagerVisitor implements UnitVisitor {

private static final Logger LOGGER = LoggerFactory.getLogger(ManagerVisitor.class);
@Override
public void visitEngineer(Engineer engineer) {
}
@Override
public void visitBoss(Boss boss) {
}
@Override
public void visitManager(Manager manager) {
LOGGER.info("你好,{}", manager);
}
}

EngineerVisitor.java


public class EngineerVisitor implements UnitVisitor {

private static final Logger LOGGER = LoggerFactory.getLogger(EngineerVisitor.class);
@Override
public void visitEngineer(Engineer engineer) {
LOGGER.info("你好,{}", engineer);
}

@Override
public void visitBoss(Boss boss) {
}
@Override
public void visitManager(Manager manager) {
}
}

現在來模擬一下三種訪問者訪問對三種角色的訪問場景,一個部門主管領導兩個經理,每個經理分別領導兩名工程師,主管及其各級下屬依次被訪問

App.java


public class Application {

public static void main(String[] args) {
Boss boss = new Boss(new Manager(new Engineer(), new Engineer(), new Engineer()), new Manager(new Engineer(), new Engineer()), new Manager(new Engineer()));
boss.beVisited(new BossVisitor());
boss.beVisited(new ManagerVisitor());
boss.beVisited(new EngineerVisitor());
}
}

總結

上面實例中的三種角色就是三種不同的數據結構,它們分別約定了訪問者對自己的訪問方式,訪問者根據被訪問者的類型實現不同的訪問算法。

所以,訪問者模式主要解決的問題是穩定的數據結構和易變的操作耦合問題,它將數據結構和數據操作分離開來。如果要對一個對象結構中的元素進行很多不同的且較為複雜的操作,而應當儘量避免讓這些操作摻雜在這些對象的類中,可以使用訪問者模式將這些封裝到訪問者的類中。實現方式為:使被訪問的類實現統一的為訪問者提供“接待”服務的接口,每個被訪問者接待方法中將自身的引用作為參數傳遞給訪問者。藉助訪問者模式,數據結構和數據操作很好的分離開來了。

本文鏈接: https://james.letec.top/2018/06/05/設計模式學習筆記(19)訪問者/


分享到:


相關文章: