首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

設計模式實作系列(二十):Visitor 訪問者模式 - 雙重分派實現操作與結構分離

     編輯:關於C++

模式意圖

表示一個作用於某對象結構中各元素的操作。它使你可以在不改變各元素類別的前提下,定義作用於這些元素的新操作。

UML 結構圖

Visitor Pattern UML

核心解析

  • 雙重分派機制: 客戶端先呼叫 Element::Accept(Visitor&),再由具體元素呼叫 Visitor::VisitConcreteElement(this)。兩次動態綁定確定具體操作。
  • 優點: 新增操作只需新增 Visitor 子類,不修改元素類別;相關操作集中在 Visitor 中,符合單一職責。
  • 缺點: 新增元素類別極其困難,需修改 Visitor 介面及所有子類,違反開放封閉原則。適用於「元素結構穩定、操作頻繁變化」的場景。
  • 破壞封裝: Visitor 往往需存取元素內部狀態,可能迫使元素開放原本私有的資料。

C++ 完整實作

1. Visitor.h - 介面定義

#ifndef VISITOR_H
#define VISITOR_H

class Visitor;

// 元素抽象基類
class Element {
public:
    virtual ~Element() {}
    virtual void Accept(Visitor& rVisitor) = 0;
protected:
    Element() {}
};

// 具體元素 A
class ConcreteElementA : public Element {
public:
    virtual ~ConcreteElementA() {}
    virtual void Accept(Visitor& rVisitor);
};

// 具體元素 B
class ConcreteElementB : public Element {
public:
    virtual ~ConcreteElementB() {}
    virtual void Accept(Visitor& rVisitor);
};

// 訪問者抽象基類:針對每種元素宣告訪問介面
class Visitor {
public:
    virtual ~Visitor() {}
    virtual void VisitConcreteElementA(ConcreteElementA* p) = 0;
    virtual void VisitConcreteElementB(ConcreteElementB* p) = 0;
protected:
    Visitor() {}
};

// 具體訪問者 A
class ConcreteVisitorA : public Visitor {
public:
    virtual ~ConcreteVisitorA() {}
    virtual void VisitConcreteElementA(ConcreteElementA* p);
    virtual void VisitConcreteElementB(ConcreteElementB* p);
};

// 具體訪問者 B
class ConcreteVisitorB : public Visitor {
public:
    virtual ~ConcreteVisitorB() {}
    virtual void VisitConcreteElementA(ConcreteElementA* p);
    virtual void VisitConcreteElementB(ConcreteElementB* p);
};

#endif

2. Visitor.cpp - 邏輯實作

#include "Visitor.h"
#include <iostream>

void ConcreteElementA::Accept(Visitor& rVisitor) {
    rVisitor.VisitConcreteElementA(this);
}

void ConcreteElementB::Accept(Visitor& rVisitor) {
    rVisitor.VisitConcreteElementB(this);
}

void ConcreteVisitorA::VisitConcreteElementA(ConcreteElementA* p) {
    std::cout << "VisitConcreteElementA By ConcreteVisitorA\n";
}

void ConcreteVisitorA::VisitConcreteElementB(ConcreteElementB* p) {
    std::cout << "VisitConcreteElementB By ConcreteVisitorA\n";
}

void ConcreteVisitorB::VisitConcreteElementA(ConcreteElementA* p) {
    std::cout << "VisitConcreteElementA By ConcreteVisitorB\n";
}

void ConcreteVisitorB::VisitConcreteElementB(ConcreteElementB* p) {
    std::cout << "VisitConcreteElementB By ConcreteVisitorB\n";
}

3. Main.cpp - 客戶端測試

#include "Visitor.h"

int main() {
    Visitor* pVisitorA = new ConcreteVisitorA();
    Element* pElement = new ConcreteElementA();

    pElement->Accept(*pVisitorA); // 雙重分派觸發

    delete pElement;
    delete pVisitorA;
    return 0;
}

適用場景與建議

  • 物件結構固定(如編譯器 AST 節點、文檔模型),但需頻繁新增分析操作(型別檢查、代碼生成、優化遍歷)。
  • 若元素類別須頻繁新增,請改用 虛擬函數 或 模式匹配(C++17 std::variant + std::visit)替代。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved