初識設計模式 – 訪問者模式

簡介

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