《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。
设想一个场景,就是学校,学校里有老师和学生,老师和学生可以称为两种元素,我们可以对这些元素进行很多操作(注意,这些操作都是外部性质的,不属于元素本身,这个至关重要),比如评价,问卷调查,采访和体检等,如果我们把这些操作定义在元素层次,显然不合理,一是这些操作不是元素对象的固有属性和行为,而是一些外部操作,不符合面向对象的封装和责任单一原则。如果把这些操作定义在学校这层,显然也不合理,因为学校的责任只是对老师和学生进行管理,这些操作也不是学校的职责。既然不能定义在元素层和学校层,我们可以单独定义一个层次结构来实现,但这里面有两个问题,一是这些操作可能对不同的元素有不同的形式,二是这些操作还必须得到元素的许可,即元素可以接收你的访问。上面的场景其实就是访问者模式的典型应用场景,下面访问者的简图:
上面场景的实现代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace DesignModelStudy
{
public abstract class Person
{
public abstract void Accept(Person_Visitor VA);
public abstract int GetPersonType();
}
//Studeng对象.
class Student : Person
{
private int _id;
private string _name;
public override int GetPersonType()
{
return 1;
}
public Student(int ID, string Name)
{
this._name = Name;
this._id = ID;
}
public int ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
public string Name
{
get
{
return this._name;
}
set
{
this._name = value;
}
}
public override void Accept(Person_Visitor VA)
{
System.Windows.Forms.MessageBox.Show(VA.Visit(this));
}
}
//Teacher对象.
class Teacher : Person
{
private int _id;
private string _name;
private int _age;
public Teacher(int ID, string Name,int Age)
{
this._name = Name;
this._id = ID;
this._age = Age;
}
public int ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
public string Name
{
get
{
return this._name;
}
set
{
this._name = value;
}
}
public int Age
{
get
{
return this._age;
}
set
{
this._age = value;
}
}
public override void Accept(Person_Visitor VA)
{
System.Windows.Forms.MessageBox.Show(VA.Visit(this));
}
public override int GetPersonType()
{
return 2;
}
}
//人员管理类
class School
{
private System.Collections.ArrayList _students;
public School()
{
_students = new System.Collections.ArrayList();
}
public void Add(Person st)
{
if (this._students.IndexOf(st) < 0)
{
this._students.Add(st);
}
}
public void Del(Person st)
{
this._students.Remove(st);
}
public Person GetStudent(int index)
{
if (index >= 0 && index < this._students.Count)
{
return (Person)this._students[index];
}
return null;
}
public Person this[int index]
{
get
{
if (index >= 0 && index < this._students.Count)
{
return (Person)this._students[index];
}
return null;
}
}
public int Count
{
get
{
return this._students.Count;
}
}
}
public abstract class Person_Visitor
{
public abstract string Visit(Person p);
}
//对人员进行评价
public class EvaluateVisit : Person_Visitor
{
public override string Visit(Person p)
{
if (p is Student)
{
return "学生:" + ((Student)p).Name + " 评价" + " 结果:10";
}
else
{
return "老师:" + ((Teacher)p).Name + "" + " 年龄:" + ((Teacher)p).Age.ToString() + " 评价结果:30";
}
}
}
//对人员进行采访
public class InterviewVisit : Person_Visitor
{
public override string Visit(Person p)
{
if (p is Student)
{
return "学生:" + ((Student)p).Name + " 已经采访" + " 结果:10";
}
else
{
return "老师:" + ((Teacher)p).Name + "已经采访" + " 年龄:" + ((Teacher)p).Age.ToString();
}
}
}
public class VisitorTest
{
private School _pMgr;
public VisitorTest()
{
_pMgr = new School();
}
public void CreateDatas()
{
Random rd = new Random();
for (int i = 0; i < 10; i++)
{
if ((i % 2) == 1)
{
this._pMgr.Add(new Student(i, "学生" + i.ToString()));
}
else
{
this._pMgr.Add(new Teacher(i, "老师" + i.ToString(), 30 + Convert.ToInt32(rd.NextDouble() * 100)));
}
}
}
public void Test()
{
EvaluateVisit ev = new EvaluateVisit();
for (int i=0;i<this._pMgr.Count;i++)
{
this._pMgr[i].Accept(ev);
}
}
}
}
总结:
1)访问者模式提供了一种将数据结构和附加到该数据结构的操作进行分离解耦的方法,当然,要使用访问者模式,有一个必要条件就是数据元素要比较多而且进行集合式管理。原因非常简单,如果数据结构的元素数目比较少,而且不能进行批处理(集合的好处就是可以进行批处理),利用访问者模式就没有什么优势。
2)访问者模式适合于对那些元素数目多(而且能进行整体性管理,比如集合,组合,树型结构),需要进行附加操作(但操作多,变化大,不确定)处理的地方。
3)访问者模式一般跟集合,组合模式一起使用会比较多。
4)特别注意,访问者模式并不是为了将类的数据属性和操作属性分离。访问者模式提供的是一种数据元素与附加操作之间进行协调管理的一种方式。
后记:现在的编程语言中采用这种模式的地方很多,比如对流操作中的stream和reader,writer,针对可枚举对象的迭代和查询等。