2013年8月13日 星期二

設計模式:訪問者模式 (Visitor Pattern)

訪問者模式 (Visitor Pattern),以下程式碼以 C# 為例

說明:
當一個「物件結構」中的「元素」幾乎不會異動,但這些「元素的行為」常會增減,則適合用訪問者模式。
訪問者模式是將「元素的行為」,提取出來,每一種行為做成一個 「Visitor(訪問者) 物件」,
每一個 「Visitor(訪問者) 物件」,都能依據不同的「元素」,對應到不同的行為結果。

範例:
在一個遊戲結構中,有兩種人,誠實人、說謊人。
誠實人都說真話,說謊人都說假話。
誠實人、說謊人即是不會異動的元素,
但對誠實人、說謊人「提出的問題」,是可能會改變的,
所以「提出的問題」即可當作「Visitor(訪問者) 物件」,用訪問者模式 (Visitor Pattern),對以後要增減「提出的問題」是較方便的。
(但若要新增第三種人則較麻煩)

希望達成如下的效果
static void Main(string[] args)
{
    // 物件結構
    ObjectStructure o = new ObjectStructure();
    // 誠實人元素物件
    HonestManElement honestMan = new HonestManElement();
    // 說謊人元素物件
    LieManElement lieMan = new LieManElement();

    // 將誠實人、說謊人元素物件放進物件結構
    o.Attach(honestMan);
    o.Attach(lieMan);


    // 數學問題訪問者
    MathVisitor mathVisitor = new MathVisitor();
    Console.WriteLine("[數學問題]");
    o.Display(mathVisitor); // 輸出結果

    // 物理問題訪問者
    PhysicsVisitor physicsVisitor = new PhysicsVisitor();
    Console.WriteLine("[物理問題]");
    o.Display(physicsVisitor); // 輸出結果

    Console.Read();
}
執行結果:
[數學問題]
誠實人 說: 1+1=2
說謊人 說: 1+1=3
[物理問題]
誠實人 說: 鐵球在水中會沉下去
說謊人 說: 鐵球在水中會浮起來

實現的重點在於,物件往返傳了兩次,
第一次是「Visitor物件」經由「結構物件」傳給「元素物件」
第二次是「元素物件」將自己傳給「Visitor物件」,以便「Visitor物件」依不同的「元素物件」執行不同的方法。

其餘程式碼
// 物件結構
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 Display(Visitor visitor)
    {
        foreach (Element e in elements)
        {
            e.Accept(visitor);
        }
    }
}

// 元素抽像類別 (要放入物件結構中) 
abstract class Element
{
    // 每個元素要能接收訪問者物件,以便再將自己傳給訪問者
    public abstract void Accept(Visitor visitor);
}

// 誠實人元素物件
class HonestManElement : Element
{
    public string name = "誠實人";
    public override void Accept(Visitor visitor)
    {
        // 將自己傳給訪問者,以便訪問者分辨、執行適合自己的行為
        visitor.visit(this);
    }
}

// 說謊人元素物件
class LieManElement : Element
{
    public string name = "說謊人";
    public override void Accept(Visitor visitor)
    {
        // 將自己傳給訪問者,以便訪問者分辨、執行適合自己的行為
        visitor.visit(this);
    }
}


// 訪問者 (能根據不同元素,產生不同結果)
abstract class Visitor
{
    // 訪問誠實人的多載方法
    public abstract void visit(HonestManElement honestElement);

    // 訪問說謊人的多載方法
    public abstract void visit(LieManElement lieElement);
}

// 數學問題訪問者
class MathVisitor : Visitor
{
    // 訪問誠實人的多載方法
    public override void visit(HonestManElement honestElement)
    {
        Console.WriteLine("{0} 說: 1+1=2", honestElement.name);
    }

    // 訪問說謊人的多載方法
    public override void visit(LieManElement lieElement)
    {
        Console.WriteLine("{0} 說: 1+1=3", lieElement.name);
    }
}

// 物理問題訪問者
class PhysicsVisitor : Visitor
{
    // 訪問誠實人的多載方法
    public override void visit(HonestManElement honestElement)
    {
        Console.WriteLine("{0} 說: 鐵球在水中會沉下去", honestElement.name);
    }

    // 訪問說謊人的多載方法
    public override void visit(LieManElement lieElement)
    {
        Console.WriteLine("{0} 說: 鐵球在水中會浮起來", lieElement.name);
    }
}

相關連結:設計模式整理列表

沒有留言:

張貼留言