2015年1月6日 星期二

C# Design Pattern - Visitor Pattern 參觀者模式

如果需要對一個複雜系統架構(Acceptor),針對整個系統做一巡訪
並做不同的動作處理,將把這處理器(Visitor)的部份獨立出來成為物件
也就是將資料處理由資料結構中分離出來,就是Visitor Pattern。

例如,有一個檔案架構系統,希望把整個檔案目錄架構整個列印出來,
需要有一個print_Visitor是負責來做把檔案列印出來的工作,
只要把這print_Visitor丟到一個複雜架構(acceptor)內,就會達到這目的。

如果在這檔案架構內,希望可以把所有圖檔都可以產生一個縮圖
這時候,可以產生另一個thumb_Visitor,用來負責產生縮圖的工作
只要把這thumb_Visitor丟到架構內,就會達到目的。

對於一個複雜架構的系統(Acceptor),丟進去不同功能的Visitor
就可以對整個系統做不同功能的事。

實作Visitor Pattern會用到一個比較複雜的遞迴(Double dispatch)
一般遞迴是自己呼叫自己,在這裡面是Acceptor與Visitor的彼此呼叫遞迴


參與者
1.Acceptor - 內需要定義一個accept method,這method只是都固定用來把自己當作參數,呼叫啟動visitor的巡訪動作:

public Class Acceptor {
        public void accept(Visitor v){
    v.visit(this)
        };
        .....

}

2.Visitor - 程式的運作核心是在Visitor的visit function:
public Class Visitor{
    public void visit(Acceptor entry){
        Iterator it=entry.iterator()
        while(it.hasNext()){
            Acceptor en=it.next();
            en.accept(this);
        }
    }
}

3.Main - 外部程式

Acceptor rootdir=new Acceptor("root");
rootdir.accept(new Visitor());

4.在Visitor實作迴圈上
private String currentdir=""
public void visit(Directory dir){
    String savedir=currentdir+"/"+dir.getName()
    Iterator it=dir.iterator();
    while(it.hasNext()){
        Entry entry=(Entry)it.next();
        entry.accept(this)
    }
    currentdir=savedir;
}

下面操作表示可以在物體結構的元素進行。訪客可以讓你定義一個新的操作,而不改變其運作上的元素的類。




進階的運用,和角色類和參與這個模式的對象可以更細分如下:

Visitor  (Visitor)
聲明在對象結構中的每個類ConcreteElement的訪問操作。操作的名稱和簽名識別發送訪問請求,訪問者的類。讓參觀者確定具體類的元素被訪問。那麼Visitor可以通過其特定的界面直接訪問的元素.。

ConcreteVisitor(IncomeVisitor,VacationVisitor)
實現由觀眾宣布每個操作。每個操作實現為在結構中相應的類或對象中定義的算法的一個片段。 ConcreteVisitor提供了上下文的算法和存儲其本地狀態。這種狀態經常結構的遍歷期間累積的結果。

Element  (Element)
定義一個接受的操作,需要一個Visitor作為一個參數。

ConcreteElement  (Employee)
實現了一個接受的操作,需要一個Visitor作為一個參數.

ObjectStructure  (Employees)
可以枚舉它的元素
可提供一個高層次的接口,以允許訪問者訪問其元素
可以是一個複合物(圖案)或集合,例如一個清單或一組

這種結構的代碼演示在其中一個對象遍歷對象結構,在這種結構中的每個節點上執行相同的操作的訪問者模式。不同的Visitor對象定義不同的操作。

using System;
using System.Collections.Generic;

namespace DoFactory.GangOfFour.Visitor.Structural
{
    /// <summary>
    /// MainApp startup class for Structural
    /// Visitor Design Pattern.
    /// </summary>
    class MainApp
    {
        static void Main()
        {
            // Setup structure
            ObjectStructure o = new ObjectStructure();
            o.Attach(new ConcreteElementA());
            o.Attach(new ConcreteElementB());

            // Create visitor objects
            ConcreteVisitor1 v1 = new ConcreteVisitor1();
            ConcreteVisitor2 v2 = new ConcreteVisitor2();

            // Structure accepting visitors
            o.Accept(v1);
            o.Accept(v2);

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Visitor' abstract class
    /// </summary>
    abstract class Visitor
    {
        public abstract void VisitConcreteElementA(
          ConcreteElementA concreteElementA);
        public abstract void VisitConcreteElementB(
          ConcreteElementB concreteElementB);
    }

    /// <summary>
    /// A 'ConcreteVisitor' class
    /// </summary>
    class ConcreteVisitor1 : Visitor
    {
        public override void VisitConcreteElementA(
          ConcreteElementA concreteElementA)
        {
            Console.WriteLine("{0} visited by {1}",
              concreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void VisitConcreteElementB(
          ConcreteElementB concreteElementB)
        {
            Console.WriteLine("{0} visited by {1}",
              concreteElementB.GetType().Name, this.GetType().Name);
        }
    }

    /// <summary>
    /// A 'ConcreteVisitor' class
    /// </summary>
    class ConcreteVisitor2 : Visitor
    {
        public override void VisitConcreteElementA(
          ConcreteElementA concreteElementA)
        {
            Console.WriteLine("{0} visited by {1}",
              concreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void VisitConcreteElementB(
          ConcreteElementB concreteElementB)
        {
            Console.WriteLine("{0} visited by {1}",
              concreteElementB.GetType().Name, this.GetType().Name);
        }
    }

    /// <summary>
    /// The 'Element' abstract class
    /// </summary>
    abstract class Element
    {
        public abstract void Accept(Visitor visitor);
    }

    /// <summary>
    /// A 'ConcreteElement' class
    /// </summary>
    class ConcreteElementA : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitConcreteElementA(this);
        }

        public void OperationA()
        {
        }
    }

    /// <summary>
    /// A 'ConcreteElement' class
    /// </summary>
    class ConcreteElementB : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitConcreteElementB(this);
        }

        public void OperationB()
        {
        }
    }

    /// <summary>
    /// The 'ObjectStructure' class
    /// </summary>
    class ObjectStructure
    {
        private List<Element> _elements = new List<Element>();

        public void Attach(Element element)
        {
            _elements.Add(element);
        }

        public void Detach(Element element)
        {
            _elements.Remove(element);
        }

        public void Accept(Visitor visitor)
        {
            foreach (Element element in _elements)
            {
                element.Accept(visitor);
            }
        }
    }

}

下例範例以員工假日和收入之間的關係,用Visitor來呈現其之間的關係操作。
這個代碼演示Visitor模式中,橫越兩個物體員工的名單和每個員工執行相同的操作。這兩個Visitor對象定義不同的操作 -1調整休假日和其他收入。

using System;
using System.Collections.Generic;

namespace DoFactory.GangOfFour.Visitor.RealWorld
{
    /// <summary>
    /// Visitor Design Pattern.
    /// </summary>
    class MainApp
    {
        /// <summary>
        /// 程式執行的啟動初始
        /// </summary>
        static void Main()
        {
            // 設定 employee collection
            Employees e = new Employees();
            e.Attach(new Clerk());
            e.Attach(new Director());
            e.Attach(new President());

            // Employees are 'visited'
            e.Accept(new IncomeVisitor());
            e.Accept(new VacationVisitor());

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 'Visitor' 的 interface
    /// </summary>
    interface IVisitor
    {
        void Visit(Element element);
    }

    /// <summary>
    /// 'ConcreteVisitor' 的 class
    /// </summary>
    class IncomeVisitor : IVisitor
    {
        public void Visit(Element element)
        {
            Employee employee = element as Employee;

            // Provide 10% pay raise
            employee.Income *= 1.10;
            Console.WriteLine("{0} {1}'s new income: {2:C}",
              employee.GetType().Name, employee.Name,
              employee.Income);
        }
    }

    /// <summary>
    /// 'ConcreteVisitor' 的 class
    /// </summary>
    class VacationVisitor : IVisitor
    {
        public void Visit(Element element)
        {
            Employee employee = element as Employee;

            // 提供 3 個 extra vacation days
            Console.WriteLine("{0} {1}'s new vacation days: {2}",
              employee.GetType().Name, employee.Name,
              employee.VacationDays);
        }
    }

    /// <summary>
    /// 'Element' abstract 的 class
    /// </summary>
    abstract class Element
    {
        public abstract void Accept(IVisitor visitor);
    }

    /// <summary>
    /// 'ConcreteElement' 的 class
    /// </summary>
    class Employee : Element
    {
        private string _name;
        private double _income;
        private int _vacationDays;

        // 建構函數
        public Employee(string name, double income,
          int vacationDays)
        {
            this._name = name;
            this._income = income;
            this._vacationDays = vacationDays;
        }

        // name的存取
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        // income的存取
        public double Income
        {
            get { return _income; }
            set { _income = value; }
        }

        // number of vacation days的存取
        public int VacationDays
        {
            get { return _vacationDays; }
            set { _vacationDays = value; }
        }

        public override void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    /// <summary>
    /// 'ObjectStructure' 的 class
    /// </summary>
    class Employees
    {
        private List<Employee> _employees = new List<Employee>();

        public void Attach(Employee employee)
        {
            _employees.Add(employee);
        }

        public void Detach(Employee employee)
        {
            _employees.Remove(employee);
        }

        public void Accept(IVisitor visitor)
        {
            foreach (Employee e in _employees)
            {
                e.Accept(visitor);
            }
            Console.WriteLine();
        }
    }

    // 3個 employee types

    class Clerk : Employee
    {
        // Constructor
        public Clerk()
            : base("Hank", 25000.0, 14)
        {
        }
    }

    class Director : Employee
    {
        // Constructor
        public Director()
            : base("Elly", 35000.0, 16)
        {
        }
    }

    class President : Employee
    {
        // Constructor
        public President()
            : base("Dick", 45000.0, 21)
        {
        }
    }
}

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

沒有留言:

張貼留言