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

