二十六.行为型设计模式——Visitor Pattern(访问者模式)
-
定义
说明一个操作执行于一个对象结构的成员(elements)中。访问者样式让你定义一个类的新操作而无须改变它操作的这些成员类。
UML类图如下:
其中类和对象的关系为:
1.Visitor(抽象访问者):为对象结构类中每一个ConcreteElement的类声明一个Visit操作。这个操作的名称及标志(signature)识别传出Visit请求给访问者的类。这就使得访问者可以解定正要被访问的元素的具体类,这样访问者就可以直接经由其特有接口访问到元素(Element)。
2.ConcreteVisitor(具体访问者):实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了场景并存储它的局部状态。这一状态常常在遍历该结构的过程中积累结果。
3.Element(元素):定义一个Accept操作,它以一个访问者为参数。
4.ConcreteElement(抽象具体元素):实现Accept操作,该操作以一个访问者为参数。
5.ObjectStructure(对象结构类):能枚举它的元素;可以提供一个高层的接口以允许访问者访问它的元素;可以是一个组合模式(Composite Pattern)或是一个集合,如一个列表或一个无序集合。
典型应用的顺序图如下:
-
实例1——人事评估
公司的人事评估需要人事部访问一个员工的列表,逐个对员工作出评估。人事部有两个访问者,一个访问者评估员工假期,一个访问者评估员工薪金。类图如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace ConsoleApplication
{
//抽象访问者
abstract class Visitor
{
//访问方法,以成员为参数
abstract public void Visit(Element element);
}
//具体访问者——薪水访问者
class IncomeVisitor : Visitor
{
public override void Visit(Element element)
{
Employee employee = ((Employee)element);
employee.Income *= 1.10;
Console.WriteLine("{0}新薪水:{1:C}", employee.Name, employee.Income);
}
}
//具体访问者——假期访问者
class VacationVisitor : Visitor
{
public override void Visit(Element element)
{
Employee employee = ((Employee)element);
employee.VacationDays += 3;
Console.WriteLine("{0}新假期天数:{1}", employee.Name, employee.VacationDays);
}
}
//抽象成员类
abstract class Element
{
//接受访问者方法
abstract public void Accept(Visitor visitor);
}
//具体成员——雇员类
class Employee : Element
{
string name;
double income;
int vacationDays;
public Employee(string name, double income, int vacationDays)
{
this.name = name;
this.income = income;
this.vacationDays = vacationDays;
}
public string Name
{
get { return name; }
set { name = value; }
}
public double Income
{
get { return income; }
set { income = value; }
}
public int VacationDays
{
get { return vacationDays; }
set { vacationDays = value; }
}
//重载接受访问者对象方法
public override void Accept(Visitor visitor)
{
visitor.Visit(this);
}
}
//员工聚合类
class Employees
{
private ArrayList employees = new ArrayList();
//增加员工
public void Attach(Employee employee)
{
employees.Add(employee);
}
//删除聚合中的员工
public void Detach(Employee employee)
{
employees.Remove(employee);
}
//所有聚合的员工都接受访问者
public void Accept(Visitor visitor)
{
foreach (Employee e in employees)
e.Accept(visitor);
}
}
class Program
{
static void Main(string[] args)
{
Employees e = new Employees();
e.Attach(new Employee("张三", 20000.0, 14));
e.Attach(new Employee("李四", 30000.0, 16));
e.Attach(new Employee("王五", 40000.0, 18));
//生成两个访问者对象
IncomeVisitor v1 = new IncomeVisitor();
VacationVisitor v2 = new VacationVisitor();
//聚合中的员工接受两个访问者对象
e.Accept(v1);
e.Accept(v2);
Console.Read();
}
}
}
-
优势和缺陷
Visitor(访问者)模式使得增加新的操作变得容易,它可以收集有关联的方法,而分离没有关联的方法,特别适用于分离因为不同原因而变化的事物,如“在男人中分离出男孩”。但Visitor模式常常要打破对象的封装性,visitor与element需要达成某些共识。
-
应用情景
下面的情景很适合应用访问者模式:
1.一个对象的结构包含多个不同接口的对象,而且需要根据具体对象作不同的处理。
2.对结构中的对象有很多不同且没有联系的处理,因此需要避免操作将类分离。
3.类中定义的对象结构很少改变,但你需要经常地定义处理结构的新操作。