訪問者模式被認為是最複雜的設計模式,並且使用頻率不高。大多情況下,你不需要使用訪問者模式,但是一旦需要使用它時,那就真的需要使用了。 ...
簡介
訪問者設計模式(Visitor Design Pattern)的定義是,允許一個或多個操作應用到一組對象上,解耦操作和對象本身。
在使用訪問者模式的時候,被訪問的元素通常不是單獨存在的,它們存儲在一個集合中,這個集合稱為“對象結構”,訪問者通過遍歷對象結構實現對其存儲的元素進行逐個訪問。
訪問者模式使用了“雙重分派”的調用機制,即元素對象定義一個操作方法支持註入訪問者對象,在操作方法內調用訪問者的訪問方法,並將當前元素對象傳入到訪問方法中。
具體實現
在這裡舉一個工作當中的具體例子,在小公司的項目組當中,名義上區分了開發、測試等崗位,但實際上開發人員既要會開發,也有會測試,對於測試人員也是同樣的要求,既要會測試,也要會開發。
在這裡案例當中,開發人員、測試人員統稱為元素,我們在這裡先構建一個抽象的元素類。其代碼示例如下:
public interface Element {
// 定義一個接受訪問者訪問的抽象方法
void accept(Visitor visitor);
}
對於開發人員類,根據自己的情況實現這個 accept()
方法,其代碼如下:
public class Programmer implements Element {
private String name = "開發人員";
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visitProgrammer(this);
}
}
對於測試人員,根據自己的情況實現這個 accept()
方法,其代碼如下:
public class Tester implements Element {
private String name = "測試人員";
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visitTester(this);
}
}
第二步,最重要的就是要定義好一個訪問者類,在這裡抽象出的訪問者介面可以是以項目組為範圍,為項目組中的每一個元素定義對應的訪問方法。其代碼示例如下:
public interface Visitor {
void visitProgrammer(Programmer programmer);
void visitTester(Tester tester);
}
每當出現一個新的操作時,就可以實現訪問者介面,註入不同的元素對象以實現不同的操作。
如下是開發人員和測試人員使用開發技能的代碼示例:
public class DevelopVisitor implements Visitor {
@Override
public void visitProgrammer(Programmer programmer) {
System.out.println(programmer.getName() + "在開發");
}
@Override
public void visitTester(Tester tester) {
System.out.println(tester.getName() + "在開發");
}
}
如下是開發人員和測試人員使用測試技能的代碼示例:
public class TestVisitor implements Visitor {
@Override
public void visitProgrammer(Programmer programmer) {
System.out.println(programmer.getName() + "在測試");
}
@Override
public void visitTester(Tester tester) {
System.out.println(tester.getName() + "在測試");
}
}
總結
優點
訪問者模式的主要優點如下:
- 能夠在不修改對象結構中的元素的情況下,為對象結構中的元素添加新的功能,符合開閉原則
- 將有關元素的行為都封裝到一個訪問者對象中,每個訪問者對象的功能都比較單一,符合單一職責原則
缺點
訪問者模式的主要缺點如下:
- 增加新的元素類需要在每一個訪問者類中都增加相應的具體操作,這違背了開閉原則
- 訪問者對象可以訪問並調用每一個元素對象的操作,這意味著元素對象有時候會暴露一些內部操作和內部狀態,破壞了封裝
- 訪問者模式依賴了具體類,而沒有依賴抽象類,違反了依賴倒置原則
適用場景
訪問者模式的適用場景如下:
- 對象結構中元素對象的類很少改變,但經常需要在此對象結構上定義新的操作
源碼
訪問者模式提供一個方便的可維護的方式來操作一組對象,JDK 內置了這樣的元素介面和訪問者介面。
如下是元素介面 javax.lang.model.element.Element
的部分代碼:
public interface Element extends javax.lang.model.AnnotatedConstruct {
<R, P> R accept(ElementVisitor<R, P> v, P p);
}
如下是訪問者介面 javax.lang.model.element.ElementVisitor
的部分代碼:
public interface ElementVisitor<R, P> {
R visit(Element e, P p);
default R visit(Element e) {
return visit(e, null);
}
}