1、類層次結構的變化
類層次結構中可能常常因爲引入新的操做,從而將類型變得脆弱...
2、動機(Motivation)
1)在軟件構建過程當中,因爲需求的改變,某些類層次結構中經常須要增長新的行爲(方法),若是直接在基類中作這樣的更改,將會給子類帶來很繁重的變動負擔,甚至破壞原有設計。
2)如何在不更改類層次結構的前提下,在運行時根據須要透明地爲類層次結構上的各個類動態添加新的操做,從而避免上述問題?
3、意圖(Intent)
表示一個做用於某對象結構中的各元素的操做。它能夠在不改變各元素的類的前提下定義做用於這些元素的新的操做。
——《設計模式》GoF
4、實例:圖形的繪製問題
1)需求的改變
//繪製圖形
public abstract class Shape
{
public abstract void Draw();
/*問題:如今要在基類中添加一個方法:將圖形總體移動
public abstract void MoveTo(Point p);
因爲Shape中新增了MoveTo方法,其各個子類將不得不隨之更改
添加了這個方法後,全部的子類都要去override這個方法
可是矩形的移動和其餘圖形的移動是不一樣的,好比說移動圓只要移動圓心就能夠了
而移動矩形能夠須要移動對角線。還有一個可能就是子類不須要移動操做*/
}設計模式
//矩形
public class Rectangle : Shape
{
public override void Draw()
{
//...
}
}app
//圓形
public class Circle : Shape
{
public override void Draw()
{
//...
}
}ide
//直線
public class Line : Shape
{
public override void Draw()
{
//...
}
}this
2)程序的演變
public abstract class Shape
{
public abstract void Draw();
//預料到未來可能會引入新的操做
public abstract void Accept(ShapeVisitor v);
}spa
public abstract class ShapeVisitor
{
//重載了一系列方法,將Shape的子類做爲參數,可是這個Visit也存在改變,這是一個弊端
//Visit的參數不是多態的,是具體類型
public abstract void Visit(Rectangle shape);//#1
public abstract void Visit(Circle shape); //#2
public abstract void Visit(Line shape); //#3
}設計
//矩形
public class Rectangle : Shape
{
public override void Draw()
{
//...
}
public override void Accept(ShapeVisitor v)
{
//v是一個多態的,this是一個指針
v.Visit(this); //調用#1方法
}
}指針
//圓形
public class Circle : Shape
{
public override void Draw()
{
//...
}
public override void Accept(ShapeVisitor v)
{
v.Visit(this); //調用#2方法
}
}對象
//直線
public class Line : Shape
{
public override void Draw()
{
//...
}
public override void Accept(ShapeVisitor v)
{
v.Visit(this); //調用#3方法 //第二次多態辨析
}
}it
public class MyVisitor :ShapeVisitor
{
public override void Visit(Rectangle shape)//#1
{
//增長對Rectangle的操做
}
public override void Visit(Circle shape) //#2
{
//增長對Circle的操做
}
public override void Visit(Line shape) //#3
{
//增長對Line的操做
}
/*爲何Visit的參數是具體類型,而不用抽像類型:用具體類型重載方
**法是讓編繹器辨析,而用抽像類型則須要咱們本身辨析。
public override void Visit(Shape shape)
{
if (shape is Rectangle)
{
//增長對Rectangle的操做
}
else if (shape is Circle)
{
//增長對Circle的操做
}
else if (shape is Line)
{
//增長對Line的操做
}
}
**這裏面換湯不換藥,仍是不能解決Shape抽象類添加子類的問題,它必須穩定
**/
}io
//應用
public 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);//第一次多態辨析
}
}
/*動態的添加方法
**App app = new App(new MyVisitor());
**app.Process(new Line);
*/
5、Visitor模式的幾個要點 1)Visitor模式經過所謂雙重分發(double dispatch)來實如今不更改Element類層次結構的前提下,在運行時透明地爲類層次結構上的各個類動態添加新的操做。 2)所謂雙重分發即Visitor模式中間包括了兩個多態分發(注意其中的多態機制):第一個爲accept方法的多態辨析;第二個爲visit方法的多態辨析。 3)Visitor模式的最大缺點在於擴展類層次結構(增添新的Element子類),會致使Visitor類的改變。所以Visitor模式適用於「Element類層次結構穩定,而其中的操做卻常常而臨頻繁改動」。 分析:任何一個設計模式都不可能徹底解決問題,即便是幾個設計模式組合也不可能,由於每個設計模式都是假定了一個變化和一個穩定,若是假定的穩定不成立,那麼設計模式將是一個誤用,由於它不能解決問題。