访问者(Visitor) 模式 作用于某个对象群中各个对象的操作 . 它可以使你在不改变这些对象本身的情况下 , 定义作用于这些对象的新操作 .Visitor 模式实际上是分离了 collection 结构中的元素和对这些元 素进行操作的行为 .
IDictionaryEnumerator iterator
=
collection. GetEnumerator ()
while
(iterator. MoveNext ())
{ Object o = iterator. Current; if (o is Hashtable) messyPrintCollection((Hashtable)o); else if (o is String) Console.WriteLine( " ' " + o.ToString() + " ' " ); else if (o is Float) Console.WriteLine (o.ToString() + " f " ); else Console.WriteLine (o.ToString());
很显然 , 这样做的缺点代码 If else if 很繁琐 . 我们就可以使用 Visitor 模式解决它 .
针对上例 , 定义接口叫 Visitable, 用来定义一个 accept 操作 , 也就是说让 Collection 每个元素具备可访问性 .
被访问者是我们 Collection 的每个元素 Element, 我们要为这些 Element 定义一个可以接受访问的接口 ( 访问和被访问是互动的 , 只访问者 , 被访问者如果表示不欢迎 , 访问者就不能访问 ), 取名为 Visitable.
被访问的具体元素继承这个新的接口 Visitable :
using
System;
using
System.Collections;
namespace
ConsoleApplication4
{ public interface Visitable { void accept(Visitor visitor); } public class StringElement : Visitable { private String text = "" ; public StringElement(String txt) { this .text = txt; } public String Text { get { return this .text; } } Visitable #region Visitable public void accept(Visitor visitor) { // TODO: visitor.visitString( this ); } #endregion } public class FloatElement : Visitable { private float number = 0.0f ; public FloatElement( float tmp) { this .number = tmp; } public float Number { get { return this .number; } } Visitable #region Visitable public void accept(Visitor visitor) { // TODO: visitor.visitFloat( this ); } #endregion } public interface Visitor { void visitString(StringElement stringE); void visitFloat(FloatElement floatE); void visitCollection(ArrayList collection); } /**/ /// <summary> /// ConcreteVisitor /// </summary> public class ConcreteVisitor : Visitor { public ConcreteVisitor() { // // TODO: // } Visitor #region Visitor public void visitString(StringElement stringE) { // TODO: Console.WriteLine(stringE.Text); } public void visitFloat(FloatElement floatE) { Console.WriteLine(floatE.Number); } public void visitCollection(ArrayList collection) { System.Collections.IEnumerator iterator = collection.GetEnumerator(); while (iterator.MoveNext()) { object o = iterator.Current; if (o is Visitable) { ((Visitable)o).accept( this ); } } } #endregion } }
using
System;
using
System.Collections;
namespace
ConsoleApplication4
{ /**/ /// <summary> /// Class1 /// </summary> class Class1 { /**/ /// <summary> /// /// </summary> [STAThread] static void Main( string [] args) { // // TODO: // Visitor visitor = new ConcreteVisitor(); StringElement stringE = new StringElement( " Hello,World " ); visitor.visitString(stringE); FloatElement floatE = new FloatElement( 4.2F ); visitor.visitFloat(floatE); System.Collections.ArrayList list = new ArrayList(); list.Add( new StringElement( " string1 " )); list.Add( new StringElement( " string2 " )); list.Add( new FloatElement( 3.4f )); list.Add( new StringElement( " string3 " )); visitor.visitCollection(list); Console.Read(); } } }
我们设计一个接口 visitor 访问者,在这个接口中 , 有一些访问操作,这些访问操作是专门访问对象集合 Collection 中有可能的所有类,目前我们假定有三个行为:访问对象集合中的字符串类型;访问对象集合中的 Float 类型;
StringElement只是一个实现,可以拓展为更多的实现,整个核心奥妙在 accept 方法中,在遍历 Collection 时,通过相应的 accept 方法调用具体类型的被访问者。这一步确定了被访问者类型,
如果是 StringElement ,而 StringElement 则回调访问者的 visiteString 方法,这一步实现了行为操作方法。
客户端代码中的 list 对象集合中放置了多种数据类型,对对象集合中的访问不必象一开始那样,使用 is 逐个判断,而是通过访问者模式巧妙实现了。
使用访问者模式是对象群结构中 (Collection) 中的对象类型很少改变。 在两个接口 Visitor 和 Visitable 中 , 确保 Visitable 很少变化 , 也就是说,确保不能老有新的 Element 元素类型加进来,可以变化的是访问者行为或操作,也就是 Visitor 的不同子类可以有多种 , 这样使用访问者模式最方便 .
如果对象集合中的对象集合经常有变化 , 那么不但 Visitor 实现要变化, Visistable 也要增加相应行为, GOF 建议是 , 不如在这些对象类中直接逐个定义操作,无需使用访问者设计模式 .