存取者設計模式(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);
}
}