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

来源:https://www.cnblogs.com/coodream2009/archive/2023/10/05/17743778.html
-Advertisement-
Play Games

軟體設計原則 GRASP 通用職責分配軟體模式 來自 Craig Larman 的軟體設計書《UML 和模式應用》,Larman 在書中提出軟體設計的關鍵任務是職責分配,並提煉總結出 9 種 (5 種核心 +4 種擴展) 軟體職責分配模式,這些模式是比 GoF 設計模式更抽象的元模式。 信息專家 ( ...


訪問者模式(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 小結

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經過版本更新,Mini API 的功能逐步完善,早期支持得不太好的 mini API 現在許多特性都可以用了,比如灰常重要的依賴註入。 咱們先來個相當簡單的註入測試。來,定義一個服務類,為了偷懶,老周這裡就不使用 介面 + 實現類 的方式了。 public class MyService : IDi ...
  • 眾所周知,在Cortex-M內核中,系統節拍由Systick時鐘提供,當配置好系統滴答時鐘後,每次時鐘中斷就會觸發中斷處理函數 xPortSysTickHandler(), void xPortSysTickHandler( void ) { /* The SysTick runs at the l ...
  • 搭建msf 官方有提供一鍵安裝腳本,如下: curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate ...
  • MySQL 是世界上最流行的開源關係型資料庫管理系統之一,而其中的存儲引擎則是其關鍵組成部分之一。InnoDB 存儲引擎在 MySQL 中扮演了重要角色,提供了許多高級功能和性能優化,適用於各種應用程式和工作負載。本文將深入介紹 InnoDB 存儲引擎的各個方面,以幫助您更好地理解它的特性和優勢。 ...
  • 1. 為什麼要拆分資料庫? 單體項目在構建之初,資料庫的負載和數據量都不大,所以不需要對資料庫做拆分,小型財務系統、文書系統、ERP系統、OA系統,用一個MySQL資料庫實例基本就夠用了。 就像《淘寶技術這十年》裡面說到的,電商業務的數據量增長飛快,所以最開始的PHP+MySQL的架構已經不能滿足實 ...
  • 【Flutter】如何優美地實現一個懸浮NavigationBar 最近寫代碼的時候遇到了一個如下的需求: 整體來說,底部的條是一個浮動的懸浮窗,有如下的三個按鈕: 點擊左邊的要進入“主頁” 點擊中間的按鈕要進行頁面跳轉,能夠進入“創作頁” 點擊右邊的按鈕切換到“個人中心”頁 使用Overlay來實 ...
  • 經過 Adobe 工程師多年來的努力,並與 Chrome 等瀏覽器供應商密切合作,通過 WebAssembly + Emscripten、Web Components + Lit、Service Workers + Workbox 和新的 Web API 的支持,終於在近期推出了 Web 版 Pho ...
  • 主要增加對容器創建 MySQL、Mongo 時對 數據文件,日誌,配置,網路,時區,埠映射,密碼 的配置,更貼合生產環境的實際使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...