2008/11/21

Visitor訪問者模式

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

括號是我加的,做為註記

來源:http://www.wewill.cn/n20563c53.aspx

動機:

在軟體構建過程中,由於需求的改變,某些類層次(類別)結構中常常需要增加新的行為(方法),如果直接在基類(基礎類別)中做這樣的改變,將會給子類(衍生類別)帶來很繁重的變更負擔,甚至破壞原有設計。


如何在不更改類層次結構的前提下,在運行時根據需要透明的為類層次結構上的各個類動態添加新的操作,從而避免上述問題?

意圖:


標識一個作用於某物件結構中的各元素的操作。它可以在不改變各元素的類的前提下定義作用於這些元素的新的操作。


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方法,那麼現在就帶來了一個問題,當我們基類的方法不能完全確定,也就是穩定的時候,我們如何來進行解藕。

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)

{

//兩處多態:

//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方法,根據傳入的圖形物件和訪問者物件來形成了兩處多態。當我們在Shape中需要增加一種方法的時候,我們不需要改寫Shape類及其派生類,我們僅僅只需要增加一種Visitor類,並將新增的Visitor類傳遞到App裡。

要點:


Visitor模式通過所謂的雙重分發(double dispatch)來實現在不更改Element類層次結構的前提下,在運行時透明的為類層次結構上的各個類動態添加新的操作。


所謂雙重分發即Visitor模式中間包括了兩個多態分發:第一個為Accept方法的多態辨析;第二個為Visit方法的多態辨析(重載)


Visitor模式最大缺點在於擴展類層次結構(添加新的Element子類),會導致Visitor類的改變,因此Visitor模式使使用者Element類層子結構穩定,而其中的操作卻經常面臨頻繁改動

當我們需要增加一個Shape的子類時,我們需要給ShapeVisitor類添加一個Visit函數,並且ShapeVisitor的每個派生類也必須添加。

沒有留言:

張貼留言