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

C# 設計模式精講:訪問者模式

     編輯:關於C#

一、訪問者模式:封裝作用於數據結構的操作

當需對包含不同類型元素的聚集執行操作,且操作細節隨元素類型異變時,往往需大量類型判斷。訪問者模式將操作封裝於訪問者對象,數據結構保持穩定,實現「數據結構」與「操作邏輯」解耦。

二、雙重分派機制

核心過程:節點調用訪問者的 Visit 方法並傳入自身 → 訪問者反向調用節點的具體操作。此「雙重分派」使操作同時基於訪問者類型與節點類型決定。

三、標準結構與角色

訪問者模式結構圖

  1. 抽象訪問者:聲明各具體元素的訪問操作介面。
  2. 具體訪問者:實現具體訪問邏輯。
  3. 抽象節點:聲明 Accept(Visitor) 方法。
  4. 具體節點:實現 Accept,調用 visitor.Visit(this)。
  5. 結構對象:遍歷所有元素,提供高層介面讓訪問者訪問每個元素。

四、結構型示例代碼


// Visitor pattern -- Structural example
using System; using System.Collections;

abstract class Visitor {
    abstract public void VisitConcreteElementA(ConcreteElementA e);
    abstract public void VisitConcreteElementB(ConcreteElementB e);
}

class ConcreteVisitor1 : Visitor {
    override public void VisitConcreteElementA(ConcreteElementA e) { Console.WriteLine("{0} visited by {1}", e, this); }
    override public void VisitConcreteElementB(ConcreteElementB e) { Console.WriteLine("{0} visited by {1}", e, this); }
}

class ConcreteVisitor2 : Visitor {
    override public void VisitConcreteElementA(ConcreteElementA e) { Console.WriteLine("{0} visited by {1}", e, this); }
    override public void VisitConcreteElementB(ConcreteElementB e) { Console.WriteLine("{0} visited by {1}", e, this); }
}

abstract class Element { abstract public void Accept(Visitor v); }

class ConcreteElementA : Element {
    public override void Accept(Visitor v) { v.VisitConcreteElementA(this); }
    public void OperationA() {}
}

class ConcreteElementB : Element {
    public override void Accept(Visitor v) { v.VisitConcreteElementB(this); }
    public void OperationB() {}
}

class ObjectStructure {
    private ArrayList elements = new ArrayList();
    public void Attach(Element e) { elements.Add(e); }
    public void Detach(Element e) { elements.Remove(e); }
    public void Accept(Visitor v) { foreach (Element e in elements) e.Accept(v); }
}

public class Client {
    public static void Main() {
        ObjectStructure o = new ObjectStructure();
        o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB());
        Visitor v1 = new ConcreteVisitor1(), v2 = new ConcreteVisitor2();
        o.Accept(v1); o.Accept(v2);
    }
}

五、實戰應用:員工薪資與假期管理


// Visitor pattern -- Real World example
using System; using System.Collections;

abstract class Visitor { abstract public void Visit(Element e); }

class IncomeVisitor : Visitor {
    public override void Visit(Element e) {
        Employee emp = (Employee)e;
        emp.Income *= 1.10; // 10% 加薪
        Console.WriteLine("{0}'s new income: {1:C}", emp.Name, emp.Income);
    }
}

class VacationVisitor : Visitor {
    public override void Visit(Element e) {
        Employee emp = (Employee)e;
        emp.VacationDays += 3; // 增加 3 天假期
        Console.WriteLine("{0}'s new vacation days: {1}", emp.Name, emp.VacationDays);
    }
}

abstract class Element { abstract public void Accept(Visitor v); }

class Employee : Element {
    string name; double income; int vacationDays;
    public Employee(string name, double income, int vacationDays) {
        this.name = name; this.income = income; this.vacationDays = vacationDays;
    }
    public string Name { get { return name; } set { name = value; } }
    public double Income { get { return income; } set { income = value; } }
    public int VacationDays { get { return vacationDays; } set { vacationDays = value; } }
    public override void Accept(Visitor v) { v.Visit(this); }
}

class Employees {
    private ArrayList employees = new ArrayList();
    public void Attach(Employee e) { employees.Add(e); }
    public void Detach(Employee e) { employees.Remove(e); }
    public void Accept(Visitor v) { foreach (Employee e in employees) e.Accept(v); }
}

public class VisitorApp {
    public static void Main() {
        Employees e = new Employees();
        e.Attach(new Employee("Hank", 25000.0, 14));
        e.Attach(new Employee("Elly", 35000.0, 16));
        e.Attach(new Employee("Dick", 45000.0, 21));
        Visitor v1 = new IncomeVisitor(), v2 = new VacationVisitor();
        e.Accept(v1); e.Accept(v2);
    }
}

六、適用場景與優缺點權衡

適用場景:

  • 數據結構相對穩定,操作易變(新增操作只需新增訪問者類)。
  • 需跨多個類層次結構訪問不同類型對象(迭代器僅能同類型)。
  • 需在訪問過程中累積狀態(訪問者內部維護)。

核心優勢:

  1. 新增操作極易(新增訪問者類)。
  2. 相關行為集中於訪問者,不分散於節點類。
  3. 可跨類層次結構訪問。
  4. 狀態累積於訪問者內部,利於維護。

主要缺點:

  1. 新增節點類極難(需修改所有訪問者介面與實現),違背開閉原則。
  2. 破壞封裝:訪問者需訪問節點內部操作/狀態,節點必須暴露內部細節。

決策原則: 僅在「數據結構穩定、操作頻繁變化」時使用。若節點類頻繁新增,請避免訪問者模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved