Visitor訪問者模式(行為型模式)

字號:

動機:
    在軟件構建過程中,由于需求的改變,某些類層次結構中常常需要增加新的行為(方法),如果直接在基類中做這樣的改變,將會給子類帶來很繁重的變更負擔,甚至破壞原有設計。
    如何在不更改類層次結構的前提下,在運行時根據需要透明的為類層次結構上的各個類動態(tài)添加新的操作,從而避免上述問題?
    意圖:
    標識一個作用于某對象結構中的各元素的操作。它可以在不改變各元素的類的前提下定義作用于這些元素的新的操作。
    public abstract class Shape
    {
    public abstract void Draw();
    public abstract void MoveTo(Point p);
    }
    public class Rectangle : Shape
    {
    public override void Draw()
    {
    }
    }
    public class Circle : Shape
    {
    public override void Draw()
    {
    }
    }
    public class Line : Shape
    {
    public override void Draw()
    {
    }
    }
    在上述代碼中,我們有一個抽象基類Shape,他定義了抽象方法Draw,Rectangle、Circle、Line繼承了Shape類的方法,并且實現了Draw方法,但是當我們需要增加一個方法MoveTo時,我們必不可少的需要在各個子類中重寫MoveTo方法,那么現在就帶來了一個問題,當我們基類的方法不能完全確定,也就是穩(wěn)定的時候,我們如何來進行解藕。
    public abstract class Shape
    {
    public abstract void Draw();
    //預料到將來會引入新的操作
    public abstract void Accept(ShapeVisitor visitor);
    }
    public abstract class ShapeVisitor
    {
    //重載的關系,Visit方法參數不是基類型,是具體類型
    public abstract void Visit(Rectangle shape);
    public abstract void Visit(Circle shape);
    public abstract void Visit(Line shape);
    }
    public class MyVisitor : ShapeVisitor
    {
    public override void Visit(Rectangle shape)
    {
    //增加對Rectangle的操作
    }
    public override void Visit(Circle shape)
    {
    //增加對Circle的操作
    }
    public override void Visit(Line shape)
    {
    //增加對Line的操作
    }
    }
    public class Rectangle : Shape
    {
    public override void Draw()
    {
    }  public override void Accept(ShapeVisitor visitor)
    {
    //這里是編譯時確定的,不是運行時確定
    //如果這個調用寫到Shape基類里,編譯器編譯的時候
    //不知道編譯那個方法。編譯會報錯,因為沒有
    //Visit(Shape shape)方法。
    visitor.Visit(this);
    }
    }
    public class Circle : Shape
    {
    public override void Draw()
    {
    }
    public override void Accept(ShapeVisitor visitor)
    {
    visitor.Visit(this);
    }
    }
    public class Line : Shape
    {
    public override void Draw()
    {
    }
    public override void Accept(ShapeVisitor visitor)
    {
    visitor.Visit(this);
    }
    }
    class App
    {
    ShapeVisitor visitor;
    public App(ShapeVisitor visitor)
    {
    this.visitor = visitor;
    }
    public void Process(Shape shape)
    {
    //Examda提示:兩處多態(tài):
    //1、Accept方法的調用對象Shape
    //2、Accept方法的參數Visitor
    shape.Accept(visitor);
    }
    }
    class Program
    {
    static void Main(string[] args)
    {
    App app = new App(new MyVisitor());
    app.Process(new Line());
    }
    }
    現在,我們在Shape類中定義了一個Accept方法,這個方法也是一個抽象方法,并且Rectangle、Circle、Line實現了Accept方法。并且Accept方法有一個參數ShapeVisitor。現在轉到ShapeVisitor類,定義了Visit方法,并且有三個重載,每個Visit方法的參數都是Shape的派生類。在Rectangle、Circle和Line三個類中,我們實現的Accept方法都是將this指針傳遞給Visit方法。
    現在有一個具體的MyVisitor類繼承于ShapeVisitor類,并且在此類中每個Visit方法的重載,根據傳遞的圖形不同做具體的動作。
    在App類中的process方法,根據傳入的圖形對象和訪問者對象來形成了兩處多態(tài)。當我們在Shape中需要增加一種方法的時候,我們不需要改寫Shape類及其派生類,我們僅僅只需要增加一種Visitor類,并將新增的Visitor類傳遞到App里。
    要點:
    Visitor模式通過所謂的雙重分發(fā)(double dispatch)來實現在不更改Element類層次結構的前提下,在運行時透明的為類層次結構上的各個類動態(tài)添加新的操作。
    所謂雙重分發(fā)即Visitor模式中間包括了兩個多態(tài)分發(fā):第一個為Accept方法的多態(tài)辨析;第二個為Visit方法的多態(tài)辨析(重載)
    Visitor模式缺點在于擴展類層次結構(添加新的Element子類),會導致Visitor類的改變,因此Visitor模式使用戶Element類層子結構穩(wěn)定,而其中的操作卻經常面臨頻繁改動。
    當我們需要增加一個Shape的子類時,我們需要給ShapeVisitor類添加一個Visit函數,并且ShapeVisitor的每個派生類也必須添加。