軟體設計模式系列之二十五——訪問者模式

cooldream2009發表於2023-10-05

訪問者模式(Visitor Pattern)是一種強大的行為型設計模式,它允許你在不改變被訪問物件的類的前提下,定義新的操作和行為。本文將詳細介紹訪問者模式,包括其定義、舉例說明、結構、實現步驟、Java程式碼實現、典型應用場景、優缺點、類似模式以及最後的小結。

1 模式的定義

訪問者模式允許你在不修改被訪問物件的類的情況下,定義並封裝一組新的操作。它通常用於處理物件結構中的元素,並能夠在不改變這些元素的類的情況下,為這些元素新增新的操作。這種模式的關鍵思想是將操作與元素分離,使得增加新操作變得相對容易。

2 舉例說明

訪問者模式的思想在日常生活中有許多應用,以下是幾個比較符合訪問者模式且為大家所熟知的例子:

博物館導覽員:在博物館中,導覽員扮演著訪問者的角色。博物館中的藝術品、展品等可以被看作是元素,而導覽員則是具體訪問者。導覽員可以根據參觀者的需求,為他們提供不同的講解、資訊或故事,而不需要改變藝術品本身。

旅遊團隊:旅遊團隊的導遊可以被看作是訪問者,而遊客可以被視為元素。導遊可以根據遊客的興趣和需求,提供不同的旅遊資訊和體驗,而不需要修改景點本身。

電子商務網站的購物車:在電子商務網站中,購物車可以被看作是物件結構,而購買的商品可以被視為元素。不同的訪問者可以執行不同的操作,例如計算總價、生成訂單等,而不需要修改商品類的程式碼。

這些例子都展示了訪問者模式的核心思想:允許在不改變元素本身的情況下,為元素執行不同的操作。這種分離關注點的設計模式在實際生活中具有廣泛的應用。

3 結構

訪問者模式由以下主要元件組成:

訪問者(Visitor):定義了要訪問的物件的介面,包括訪問不同型別物件的方法。

具體訪問者(ConcreteVisitor):實現了訪問者介面,定義了針對不同型別物件的具體操作。

元素(Element):定義了接受訪問者訪問的介面,通常包括一個 accept 方法,該方法接受訪問者作為引數。

具體元素(ConcreteElement):實現了元素介面,它包含了 accept 方法的實現,該方法將自身傳遞給訪問者以便進行操作。

物件結構(Object Structure):包含元素的集合,通常提供一個方法來遍歷這些元素,訪問者可以透過該方法訪問元素。

4 實現步驟

實現訪問者模式需要按照以下步驟進行:

定義元素介面(Element),其中包括一個接受訪問者的方法(accept 方法)。

建立具體元素類(ConcreteElement),實現元素介面,並提供具體的操作。

定義訪問者介面(Visitor),其中包括為每個具體元素型別定義的訪問方法。

建立具體訪問者類(ConcreteVisitor),實現訪問者介面,併為每個具體元素型別提供具體的訪問方法。

建立物件結構類(Object Structure),其中包含元素的集合,並提供一個方法用於訪問元素。

在客戶端程式碼中,建立具體元素的例項,將它們新增到物件結構中,並建立具體訪問者的例項。

使用訪問者物件來訪問物件結構中的元素,從而執行具體的操作。

5 程式碼實現

以下是一個使用Java編寫的訪問者模式的示例程式碼:

// Step 1: 定義元素介面
interface Animal {
    void accept(Visitor visitor);
}

// Step 2: 建立具體元素類
class Dog implements Animal {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Cat implements Animal {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// Step 3: 定義訪問者介面
interface Visitor {
    void visit(Dog dog);
    void visit(Cat cat);
}

// Step 4: 建立具體訪問者類
class HealthCheckupVisitor implements Visitor {
    @Override
    public void visit(Dog dog) {
        System.out.println("健康檢查狗:" + dog.getClass().getSimpleName());
    }

    @Override
    public void visit(Cat cat) {
        System.out.println("健康檢查貓:" + cat.getClass().getSimpleName());
    }
}

class InformationDisplayVisitor implements Visitor {
    @Override
    public void visit(Dog dog) {
        System.out.println("展示狗資訊:" + dog.getClass().getSimpleName());
    }

    @Override
    public void visit(Cat cat) {
        System.out.println("展示貓資訊:" + cat.getClass().getSimpleName());
    }
}

// Step 5: 建立物件結構類
class Zoo {
    private List<Animal> animals = new ArrayList<>();

    public void addAnimal(Animal animal) {
        animals.add(animal);
    }

    public void accept(Visitor visitor) {
        for (Animal animal : animals) {
            animal.accept(visitor);
        }
    }
}

// Step 6: 客戶端程式碼
public class VisitorPatternExample {
    public static void main(String[] args) {
        Zoo zoo = new Zoo();
        zoo.addAnimal(new Dog());
        zoo.addAnimal(new Cat());

        Visitor healthCheckupVisitor = new HealthCheckupVisitor();
        Visitor informationDisplayVisitor = new InformationDisplayVisitor();

        zoo.accept(healthCheckupVisitor);
        zoo.accept(informationDisplayVisitor);
    }
}

6 典型應用場景

訪問者模式允許你在不修改現有物件結構的情況下,定義新操作並將其應用於這些物件。以下是一些典型的訪問者模式應用場景:

  • 資料結構與操作分離。當你有一個複雜的資料結構,其中包含多種不同型別的物件,並且希望對這些物件執行各種操作,但不希望將操作的程式碼放在這些物件中時,訪問者模式可以幫助你將操作與資料結構分離開來。

  • 資料結構穩定但操作頻繁變化。如果資料結構相對穩定,但需要經常新增新的操作或修改現有操作,使用訪問者模式可以輕鬆地新增新的訪問者類而不必修改資料結構類。

  • 資料結構中物件型別多樣化。當你的資料結構中包含多個不同的物件型別,且你需要對每種型別執行不同的操作時,訪問者模式使得你可以輕鬆地擴充套件和管理這些操作。

  • 資料結構具有複雜的巢狀結構。如果你的資料結構是一個複雜的巢狀結構,其中物件可以包含子物件,訪問者模式可以透過遞迴遍歷整個結構,使得操作更容易實施。

  • 擴充套件性要求高。當你需要為系統提供高度可擴充套件性,以便能夠隨時新增新的操作和物件型別時,訪問者模式是一個有用的選擇,因為它使得新增新功能變得相對容易。

  • 資料結構和操作分佈在不同的類庫中。如果資料結構和操作分別位於不同的類庫中,訪問者模式可以幫助你透過定義新的訪問者來擴充套件操作,而無需修改已有的類庫。

訪問者模式適用於需要對複雜物件結構進行多種不同操作的情況,同時又要保持資料結構的穩定性和可擴充套件性的需求。透過將操作封裝在訪問者物件中,它可以有效地解耦操作和資料結構,使得系統更加靈活和可維護。

7 優缺點

優點:

可擴充套件性。訪問者模式使得新增新的操作變得容易,無需修改已有的元素類。
分離關注點。訪問者模式將物件結構和操作分離,使得每個部分都可以獨立變化,提高了程式碼的可維護性。
靈活性。可以定義多個不同的訪問者,每個訪問者執行不同的操作,從而實現靈活的行為擴充套件。
符合開閉原則。可以在不修改已有程式碼的情況下新增新的訪問者和操作。

缺點:

增加複雜性。引入了訪問者和元素之間的額外層次,可能會增加程式碼的複雜性。
不適用於小規模場景。在小規模場景下,使用訪問者模式可能會顯得繁瑣和過於複雜。

8 類似模式

與訪問者模式類似的模式包括以下幾種:

  • 迭代器模式(Iterator Pattern):

迭代器模式和訪問者模式都用於處理集合或物件結構中的元素。它們都允許你遍歷集合中的元素,但它們的焦點不同。迭代器模式關注於提供一種訪問元素的方法,而訪問者模式關注於在元素上執行不同的操作。在迭代器模式中,通常有一個迭代器物件,它負責遍歷集合並提供對元素的訪問。而在訪問者模式中,訪問者物件負責定義要執行的操作,並遍歷物件結構來執行這些操作。

  • 組合模式(Composite Pattern):

組合模式和訪問者模式通常一起使用,以便在物件結構中執行操作。組合模式用於表示樹形結構,而訪問者模式用於在樹形結構中執行操作。組合模式主要用於建立和管理樹形結構,它使得可以像對待單個物件一樣對待組合物件。訪問者模式則用於在樹形結構中執行不同的操作,將操作與物件分離。

  • 觀察者模式(Observer Pattern):

觀察者模式和訪問者模式都屬於行為型設計模式,它們都涉及多個物件之間的互動。觀察者模式用於定義物件之間的一對多依賴關係,一個物件的狀態變化會通知所有依賴它的物件。訪問者模式用於在物件結構中執行不同的操作,與物件的狀態變化無關。

這些模式之間的聯絡在於它們都處理物件之間的關係,但它們的焦點和用途不同。訪問者模式主要用於在物件結構中執行不同的操作,而其他模式則更關注物件之間的互動、結構或組織。根據具體的問題和需求,你可以選擇使用適合的模式來改善設計和實現。

9 小結

訪問者模式是一種強大的設計模式,它可以使你輕鬆地新增新的操作,而不需要修改現有的元素類。透過將操作從元素類中分離出來,訪問者模式提高了程式碼的可維護性和可擴充套件性。然而,它也可能會引入額外的複雜性,因此在小規模場景下使用時要謹慎。瞭解訪問者模式的結構和實現步驟,以及它的優缺點和典型應用場景,將有助於你在適當的情況下使用這一模式來改善程式碼的設計和可維護性。

相關文章