背景:类层次结构的变化。类层次结构中可能经常由于引入新的操作而使类型变得脆弱。
动机(Motivation)
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
意图(Intent)
表示一个作用于某对象结构中各元素的操作,它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。
——《设计模式》GoF
示例代码
//未使用Visitor模式时出现的问题
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()
{
//...
}
}
//使用Visitor模式的代码
abstract class Shape
{
public abstract void Draw();
//预料到将来可能会引入新的操作
public abstract void Accept(ShapeVisitor visitor);
}
public abstract class ShapeVisitor
{
public abstract void Visit(Rectangle shape);
public abstract void Visit(Circle shape);
}
public class Rectangle:Shape
{
public override void Draw()
{
//...
}
public override void Accept(ShapeVisitor v)
{
v.Visit(this);
}
}
public class Circle:Shape
{
public override void Draw()
{
//...
}
public override void Accept(ShapeVisitor v)
{
v.Visit(this);
}
}
public class MyVisitor:ShapeVisitor
{
public override void Visit(Rectangle shape)
{
//增加对Rectangle的操作
}
public override void Visit(Circle shape)
{
//增加对Circle的操作
}
}
class App
{
ShapeVisitor visitor;
public App(ShapeVisitor visitor)
{
this.visitor = visitor;
}
public static void Process(Shape shape)
{
shape.Accept(visitor);
//...
}
}
/***************
App app = new App(new Myvisotor());
app.Process(new Circle());
***************/
Visitor模式的几个要点
1)Visitor模式通过所谓“双重分发”(Double dispatch)来实现在不更改Element类层次结构的前提下,在运行旱船暖为类层次结构上的各个类动态添加新的操作。
2)所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。
3)Visitor模式的最大缺点在于基类层次结构(增加新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。