《設計模式》一書對於訪問者模式給出的定義為:表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。從定義可以看出結構對象是使用訪問者模式必須條件,而且這個結構對象必須存在遍歷自身各個對象的方法。
設想一個場景,就是學校,學校裡有老師和學生,老師和學生可以稱為兩種元素,我們可以對這些元素進行很多操作(注意,這些操作都是外部性質的,不屬於元素本身,這個至關重要),比如評價,問卷調查,采訪和體檢等,如果我們把這些操作定義在元素層次,顯然不合理,一是這些操作不是元素對象的固有屬性和行為,而是一些外部操作,不符合面向對象的封裝和責任單一原則。如果把這些操作定義在學校這層,顯然也不合理,因為學校的責任只是對老師和學生進行管理,這些操作也不是學校的職責。既然不能定義在元素層和學校層,我們可以單獨定義一個層次結構來實現,但這裡面有兩個問題,一是這些操作可能對不同的元素有不同的形式,二是這些操作還必須得到元素的許可,即元素可以接收你的訪問。上面的場景其實就是訪問者模式的典型應用場景,下面訪問者的簡圖:
上面場景的實現代碼:
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,針對可枚舉對象的迭代和查詢等。