主頁 > 軟體設計 > 【訪問者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

【訪問者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

2023-05-05 09:40:57 軟體設計

簡介

訪問者模式(Visitor Pattern)是一種行為型模式,它封裝一個訪問者類,把各元素類的操作集合起來,目的是將資料結構與資料操作分離,在不改變原有元素類資料結構的前提下,改變了元素類的執行演算法,

當某些較為穩定的東西(資料結構或演算法),不想直接被改變但又想擴展功能,這時候適合用訪問者模式,訪問者模式的使用頻率并不是很高,大多數情況下,你并不需要使用訪問者模式,但是當你一旦需要使用它時,那你就是需要使用它了,

訪問者模式有以下幾個角色:

  • 結構物件(ObjectStructure):結構物件角色,這是訪問者模式的基礎角色,包含多個類或者介面.
  • 抽象元素(Element):定義一個接受訪問操作accept(),以一個訪問者Visitor作為引數,
  • 具體元素(ConcreteElement):實作抽象節點的accept()方法和處理操作,呼叫Vistor的訪問方法實作具體功能,
  • 抽象訪問者(Visitor):定義一個抽象介面,宣告一個或多個訪問操作,使得所有具體訪問者都必須實作,
  • 具體訪問者(ConcreteVisitor):具體訪問者角色,實作Visitor宣告的介面,

作用

  1. 在資料基礎類里面有一個方法接受訪問者,將自身參考傳入訪問者,從而把不變的固定起來,把變化的開放出去,
  2. 通過隔離類中變化的東西,固定不變的東西,符合單一職責原則,同時具備較好的擴展性和靈活性,

實作步驟

  1. 先創建基本元素抽象類Element,確定accept()抽象方法,
  2. 分別創建幾個具體的元素類,實作抽象元素的accept方法,
  3. 在創建Visitor抽象介面,定義visit方法,呼叫具體元素,
  4. 創建1個或多個Visitor類,繼承抽象介面,客戶將以此去訪問具體元素,
  5. 再創建物件結構類,這是核心入口類,負責組合各種元素,以及傳遞訪問者Visitor,
  6. 客戶呼叫時先創建物件結構類,再指定訪問者類,通過訪問這類呼叫具體元素類

UML

 

Java代碼

結構物件

// ObjectStructure.java 結構物件(ObjectStructure)
public class ObjectStructure {

  // 可以想象為一臺電腦,聚合了各種設備元素
  private String name = "Computer Structure";
  private List<Element> elements = new ArrayList<Element>();

  // 結構物件初始化聚合了其他元素
  public ObjectStructure() {
    addElement(new ConcreteElementA());
    addElement(new ConcreteElementB());
  }

  public void addElement(Element element) {
    elements.add(element);
  }

  // 傳入訪問者分發給其他元素
  public void accept(Visitor visitor) {
    System.out
        .println("ObjectStructure::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
            + visitor.getName() + "]");
    for (Element element : elements) {
      element.accept(visitor);
    }
  }

  public String getName() {
    return this.name;
  }

}

抽象訪問者類

// Visitor.java 訪問者Visitor抽象介面,定義不同的visit方法
public interface Visitor {
  public void visit(ConcreteElementA concreteElementA);

  public void visit(ConcreteElementB concreteElementB);

  public String getName();
}

具體訪問者

// ConcreteVisitorA.java 具體訪問者A
public class ConcreteVisitorA implements Visitor {

  // 假如由不同廠商是程式的訪問者
  private String name = "Google Visitor";

  @Override
  public void visit(ConcreteElementA concreteElementA) {
    System.out.println(
        "ConcreteVisitorA::visit() [Element.class = " + concreteElementA.getClass().getSimpleName()
            + " Element.name = "
            + concreteElementA.getName() + "]");
    concreteElementA.operate();
  }

  @Override
  public void visit(ConcreteElementB concreteElementB) {
    System.out.println("ConcreteVisitorA::visit() [Element.class = " + concreteElementB.getClass().getSimpleName()
        + " Element.name = "
        + concreteElementB.getName() + "]");
    concreteElementB.operate();
  }

  public String getName() {
    return this.name;
  }
}
// ConcreteVisitorB.java 具體訪問者B
public class ConcreteVisitorB implements Visitor {

  // 假如由不同廠商是程式的訪問者
  private String name = "Apple Visitor";

  @Override
  public void visit(ConcreteElementA concreteElementA) {
    System.out.println(
        "ConcreteVisitorB::visit() [Element.class = " + concreteElementA.getClass().getSimpleName()
            + " Element.name = "
            + concreteElementA.getName() + "]");
    concreteElementA.operate();
  }

  @Override
  public void visit(ConcreteElementB concreteElementB) {
    System.out.println(
        "ConcreteVisitorB::visit() [Element.class = " + concreteElementB.getClass().getSimpleName()
            + " Element.name = "
            + concreteElementB.getName() + "]");
    concreteElementB.operate();
  }

  public String getName() {
    return this.name;

  }
}

抽象元素類

// Element.java 抽象元素(Element),定義accept方法,傳入抽象訪問者
abstract class Element {
  public abstract void accept(Visitor visitor);
}

具體元素實作類

// ConcreteElementA.java 具體的元素實作者A
public class ConcreteElementA extends Element {
  // 可以設想為顯示幕
  private String name = "Monitor Element";

  @Override
  public void accept(Visitor visitor) {
    System.out
        .println(
            "ConcreteElementA::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
                + visitor.getName() + "]");
    visitor.visit(this);
  }

  public void operate() {
    System.out.println("ConcreteElementA::operate() [" + this.getName() + "]");
  }

  public String getName() {
    return this.name;
  }
}
// ConcreteElementB.java 具體的元素實作者B
public class ConcreteElementB extends Element {
  private String name = "Keyboard Element";

  @Override
  public void accept(Visitor visitor) {
    System.out.println(
        "ConcreteElementB::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
            + visitor.getName() + "]");
    visitor.visit(this);
  }

  public void operate() {
    System.out.println("ConcreteElementB::operate() [" + this.getName() + "]");
  }

  public String getName() {
    return this.name;
  }
}

測驗呼叫

  /**
   * 訪問者模式是當客戶需要訪問具體各元素Element時,先建立一個訪問者Visitor作為媒介
   * 客戶基于物件結構ObjectStructure,呼叫accept(),接受傳入的訪問者
   * 物件結構向其他元素負責分發訪問者,元素物件接受之后會將自己回傳給訪問者,從而訪問者可以訪問具體元素
   */
    ObjectStructure structure = new ObjectStructure();
    // 接受訪問者A,把訪問者傳遞給具體元素
    structure.accept(new ConcreteVisitorA());

    System.out.println("====");
    // 接受訪問者B,把訪問者傳遞給具體元素
    structure.accept(new ConcreteVisitorB());

Go代碼

結構物件

// ObjectStructure.go 結構物件(ObjectStructure)
type ObjectStructure struct {
  name     string
  elements []Element
}

func (o *ObjectStructure) AddElement(e Element) {
  o.elements = append(o.elements, e)
}

// 傳入訪問者分發給其他元素
func (o *ObjectStructure) Accept(v Visitor) {
  fmt.Println(
    "ObjectStructure::Accept() [Visitor.name = " +
      v.GetName() + "]")

  // 通知全部元素成員接受訪問者
  for i := 0; i < len(o.elements); i++ {
    o.elements[i].Accept(v)
  }

  // for _, ele := range o.elements {
  //   ele.Accept(v)
  // }
}

func (o *ObjectStructure) GetName() string {
  o.name = "Computer Structure"
  return o.name
}

// 結構物件的初始化函式
func (o *ObjectStructure) Init() {
  // 可以想象為一臺電腦,聚合了各種設備元素
  fmt.Println("ObjectStructure::Init() ", o.GetName())
  // 定義一個物件陣列,長度可選
  o.elements = make([]Element, 0, 100)

  // 結構物件初始化聚合了其他元素
  o.AddElement(&ConcreteElementA{})
  o.AddElement(&ConcreteElementB{})
}

抽象訪問者類

// Visitor.go 訪問者Visitor抽象介面,定義不同的visit方法
type Visitor interface {
  VisitA(e *ConcreteElementA)
  VisitB(e *ConcreteElementB)
  GetName() string
}

具體訪問者

// ConcreteVisitorA.go 具體訪問者A
type ConcreteVisitorA struct {
  name string
}

func (v *ConcreteVisitorA) GetName() string {
  v.name = "Google Visitor(struct=ConcreteVisitorA)"
  return v.name
}

func (v *ConcreteVisitorA) VisitA(e *ConcreteElementA) {
  fmt.Println(
    "ConcreteVisitorA::VisitA() [Element.name = " + e.GetName() + "]")
  e.Operate()
}

func (v *ConcreteVisitorA) VisitB(e *ConcreteElementB) {
  fmt.Println(
    "ConcreteVisitorA::VisitB() [Element.name = " + e.GetName() + "]")
  e.Operate()
}
// ConcreteVisitorB.go 具體訪問者B
type ConcreteVisitorB struct {
  name string
}

func (v *ConcreteVisitorB) GetName() string {
  v.name = "Apple Visitor(struct=ConcreteVisitorB)"
  return v.name
}

func (v *ConcreteVisitorB) VisitB(e *ConcreteElementB) {
  fmt.Println(
    "ConcreteVisitorB::VisitB() [Element.name = " + e.GetName() + "]")
  e.Operate()
}

func (v *ConcreteVisitorB) VisitA(e *ConcreteElementA) {
  fmt.Println(
    "ConcreteVisitorB::VisitA() [Element.name = " + e.GetName() + "]")
  e.Operate()
}

抽象元素類

// Element.go 抽象元素(Element),定義accept方法,傳入抽象訪問者
// go無抽象類,用interface替代
type Element interface {
  Accept(v Visitor)
  Operate()
  GetName() string
}

具體元素實作類

// ConcreteElementA.go 具體的元素實作者A
type ConcreteElementA struct {
  name string
}

func (c *ConcreteElementA) GetName() string {
  c.name = `Monitor Element(struct=ConcreteElementA)`
  return c.name
}

func (e *ConcreteElementA) Accept(v Visitor) {
  fmt.Println(
    "ConcreteElementA::Accept() [Visitor.name = " + v.GetName() + "]")
  v.VisitA(e)
}

func (e *ConcreteElementA) Operate() {
  fmt.Println("ConcreteElementA::Operate() [" + e.GetName() + "]")
}
// ConcreteElementB.go 具體的元素實作者B
type ConcreteElementB struct {
  name string
}

func (c *ConcreteElementB) GetName() string {
  c.name = "Keyboard Element(struct=ConcreteElementB)"
  return c.name
}

func (e *ConcreteElementB) Accept(v Visitor) {
  fmt.Println(
    "ConcreteElementB::Accept() [Visitor.name = " + v.GetName() + "]")
  v.VisitB(e)
}

func (e *ConcreteElementB) Operate() {
  fmt.Println("ConcreteElementB::Operate() [" + e.GetName() + "]")
}

測驗呼叫

func main() {
  fmt.Println("test start:")

  /**
   * 訪問者模式是當客戶需要訪問具體各元素Element時,先建立一個訪問者Visitor作為媒介
   * 客戶基于物件結構ObjectStructure,呼叫Accept(),接受傳入的訪問者
   * 物件結構向其他元素負責分發訪問者,元素物件接受之后會將自己回傳給訪問者,從而訪問者可以訪問具體元素
   */
  structure := src.ObjectStructure{}
  structure.Init()
  // 接受訪問者A,把訪問者傳遞給具體元素
  structure.Accept(&src.ConcreteVisitorA{})

  fmt.Println("====")
  // 接受訪問者B,把訪問者傳遞給具體元素
  structure.Accept(&src.ConcreteVisitorB{})
}

更多語言版本

不同語言設計模式原始碼:https://github.com/microwind/design-pattern

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/551663.html

標籤:設計模式

上一篇:中臺,真的是一場自欺欺人的騙局嗎?

下一篇:返回列表

標籤雲
其他(158451) Python(38117) JavaScript(25400) Java(18012) C(15221) 區塊鏈(8261) C#(7972) AI(7469) 爪哇(7425) MySQL(7157) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5871) 数组(5741) R(5409) Linux(5334) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4565) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2432) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1964) Web開發(1951) HtmlCss(1932) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1857) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【訪問者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    簡介 訪問者模式(Visitor Pattern)是一種行為型模式。它封裝一個訪問者類,把各元素類的操作集合起來,目的是將資料結構與資料操作分離。在不改變原有元素類資料結構的前提下,改變了元素類的執行演算法。 當某些較為穩定的東西(資料結構或演算法),不想直接被改變但又想擴展功能,這時候適合用訪問者模式 ......

    uj5u.com 2023-05-05 09:40:57 more
  • 中臺,真的是一場自欺欺人的騙局嗎?

    前段時間,隨著阿里集團CEO張勇的公開信發布,阿里集團也做出了歷史上最大的一次組織調整。



    隨著新的1+6+N的組織陣型的調整和落地,阿里曾經的中臺戰略,變得有點非常的尷尬了,似乎成為了一個巨大的爭議。 ......

    uj5u.com 2023-05-05 09:40:30 more
  • 我設計了個【方案】:比redis好10倍的kv庫【一統kv】

    基于ssd磁盤,此我設計了比redis更好的快取方案。此方案:沒有快取擊穿問題。沒有快取雪崩問題。沒有快取污染問題。沒有熱key問題。
    不需要snap和aof。支持任何sql庫,sql庫不需要帶有任何分布式功能。 ......

    uj5u.com 2023-05-05 09:40:21 more
  • 開發復雜軟體的系統方法(二)之軟體建模

    這幾周與公司的軟體開發專家(職稱)討論產品的軟體新架構與方案,主要涉及兩點 是否復用現有的核心機制 基于領域建模設計 關于第一點,雙方達成一致。 關于第二點,領域可以理解為業務,業務專家(產品經理,需求工程師,臨床工程師等)與研發人員一起,通過頭腦風暴、事件風暴、會議、協作等方式,使得研發人員對產品 ......

    uj5u.com 2023-05-04 08:11:46 more
  • 軟體開發、設計、架構的其他原則

    LOD:迪米特法則(Law of Demeter) CRP:合成復用原則(Composite Reuse Principle) DRY:不要重復你自己原則 (Don’t Repeat Yourself Principle) KISS:KISS原則 (Keep It Simple and Stupid ......

    uj5u.com 2023-05-04 08:11:42 more
  • 開發復雜軟體的系統方法(二)之軟體建模

    這幾周與公司的軟體開發專家(職稱)討論產品的軟體新架構與方案,主要涉及兩點 是否復用現有的核心機制 基于領域建模設計 關于第一點,雙方達成一致。 關于第二點,領域可以理解為業務,業務專家(產品經理,需求工程師,臨床工程師等)與研發人員一起,通過頭腦風暴、事件風暴、會議、協作等方式,使得研發人員對產品 ......

    uj5u.com 2023-05-04 08:11:10 more
  • 軟體開發、設計、架構的其他原則

    LOD:迪米特法則(Law of Demeter) CRP:合成復用原則(Composite Reuse Principle) DRY:不要重復你自己原則 (Don’t Repeat Yourself Principle) KISS:KISS原則 (Keep It Simple and Stupid ......

    uj5u.com 2023-05-04 08:11:05 more
  • 面向物件程式設計題目集總結blog2-22206110-胡瑞杰

    一、前言 第二次在博客園上發布面向物件程式設計題目集的總結博客。經過幾周的學習,面向物件的理念更加深入。雖然已經學了些面向物件程式設計,學好這部分內容還是有較大難度。 關于知識點 本次的題目集所體現的知識點已經不僅限于Java的語法知識,還需要考慮設計問題,不能看到題目就開始進行代碼撰寫,需要考慮類 ......

    uj5u.com 2023-05-01 07:58:17 more
  • 面向物件程式設計題目集總結blog2-22206110-胡瑞杰

    一、前言 第二次在博客園上發布面向物件程式設計題目集的總結博客。經過幾周的學習,面向物件的理念更加深入。雖然已經學了些面向物件程式設計,學好這部分內容還是有較大難度。 關于知識點 本次的題目集所體現的知識點已經不僅限于Java的語法知識,還需要考慮設計問題,不能看到題目就開始進行代碼撰寫,需要考慮類 ......

    uj5u.com 2023-05-01 07:50:55 more
  • PTA題目集1~3階段性總結

    一 前言: 題目集一:此題目集主要考察輸入陳述句和回圈陳述句以及對字串的操作,如第一到四題考察輸入輸出,第五第十一第十二考察回圈陳述句,第六題到第八題考察字串操作,題量較大且個別題目難度較大。 題目集二:此題目集主要考察對輸入資訊的判斷和數學計算,如前三題的長度質量計量單位換算和奇數求和和房地產稅費計 ......

    uj5u.com 2023-05-01 07:43:40 more