访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
下面给出访问者模式的结构图:
从类图可以看出,访问者模式那是相当复杂啊!
下面给出访问者模式的基本代码结构:
namespace ConsoleApplication1 { //Visitor类,为该对象结构中ConcreteElement的每一个类声明一个Visit操作。 abstract class Visitor { public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA); public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB); } //ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。每个操作实现算法的一部分 //而该算法片段乃是对应结构中对象的类 class ConcreteVisitor1 : Visitor { public override void VisitConcreteElementA(ConcreteElementA concreteElementA) { Console.WriteLine("{0}被{1}访问",concreteElementA.GetType().Name,this.GetType().Name); } public override void VisitConcreteElementB(ConcreteElementB concreteElementB) { Console.WriteLine("{0}被{1}访问",concreteElementB.GetType().Name,this.GetType().Name); } } class ConcreteVisitor2 : Visitor { public override void VisitConcreteElementA(ConcreteElementA concreteElementA) { Console.WriteLine("{0}被{1}访问",concreteElementA.GetType().Name,this.GetType().Name); } public override void VisitConcreteElementB(ConcreteElementB concreteElementB) { Console.WriteLine("{0}被{1}访问",concreteElementB.GetType().Name,this.GetType().Name); } } //Element类,定义一个Accepr操作,它以一个访问者为参数 abstract class Element { public abstract void Accept(Visitor visitor); } //ConcreteElementA类和ConcreteElementB类,具体元素,实现Accept类 class ConcreteElementA : Element { public override void Accept(Visitor visitor) { visitor.VisitConcreteElementA(this); } public void OperationA() {} } class ConcreteElementB : Element { public override void Accept(Visitor visitor) { visitor.VisitConcreteElementB(this); } public void OperationB() {} } //ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素 class ObjectStructure { private IList<Element> elements = new List<Element>(); public void Attach(Element element) { elements.Add(element); } public void Detach(Element element) { elements.Remove(element); } public void Accept(Visitor visitor) { foreach(Element e in elements) { e.Accept(visitor); } } } class Program { static void Main(string[] args) { ObjectStructure o = new ObjectStructure(); o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB()); ConcreteVisitor1 v1 = new ConcreteVisitor1(); ConcreteVisitor2 v2 = new ConcreteVisitor2(); o.Accept(v1); o.Accept(v2); Console.ReadKey(); } } }
输出结果如下所示:
访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。
访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较适合的,因为访问者模式使得算法操作的增加变得容易。反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,,就不适合使用访问者模式。
访问者模式的有点就是增加新的操作很容易,因为增加新的操作意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
访问者模式的缺点就是使增加新的数据结构变得困难了。
要特别注意,只要数据结构相对稳定的系统才适合用访问者模式,例如《大话设计模式》里面的男人和女人的例子,只有男人或女人。
namespace ConsoleApplication1 { abstract class Action { //得到男人的结论或反应 public abstract void GetManConclusion(Man concreteElementA); //得到女人结论或反应 public abstract void GetWomanConclusion(Woman concreteElementB); } abstract class Person { //接受 public abstract void Accept(Action visitor); } //成功 具体"状态"类 class Success : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}时,背后多半有一个伟大的女人。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}时,背后大多有一个不成功的男人。", concreteElementB.GetType().Name, this.GetType().Name); } } //失败 具体"状态"类 class Failing : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}时,闷头喝酒,谁也不用劝。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}时,眼泪汪汪,谁也劝不了。", concreteElementB.GetType().Name, this.GetType().Name); } } //恋爱 具体"状态"类 class Amativeness : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}时,凡是不懂,也要装懂。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}时,遇事懂也装不懂。", concreteElementB.GetType().Name, this.GetType().Name); } } //男人类 class Man : Person { public override void Accept(Action visitor) { visitor.GetManConclusion(this); //首先客户程序中将具体的状态作为参数传递给"男人"类完成了一次分派,然后"男人"类调用作为参数的"具体状态" //中的方法,"男人反应",同时将自己(this)作为参数传递进去。这便完成了第二次分派 } } //女人类 class Woman : Person { public override void Accept(Action visitor) { visitor.GetWomanConclusion(this); } } //对象结构 class ObjectStructure { private IList<Person> elements = new List<Person>(); //增加 public void Attach(Person element) { elements.Add(element); } //移除 public void Detach(Person element) { elements.Remove(element); } //查看显示 public void Display(Action visitor) { foreach(Person p in elements) { p.Accept(visitor); } } } class Program { static void Main(string[] args) { ObjectStructure o = new ObjectStructure(); o.Attach(new Man()); o.Attach(new Woman()); //成功时的反应 Success v1 = new Success(); o.Display(v1); //失败时的反应 Failing v2 = new Failing(); o.Display(v2); //恋爱时的反应 Amativeness v3 = new Amativeness(); o.Display(v3); Console.ReadKey(); } } }
结果如下图所示:
这样做有什么好处呢?
如果要增加一个结婚状态来考察男人和女人的反应,由于用了双分派,只需增加一个“状态”子类,就可以在客户端调用来查看,不需要改动其他任何类的代码。
//结婚状态类 class Marriage : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}时,感慨到:恋爱游戏终结时,‘有妻徒刑‘遥无期", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}时,欣慰曰:爱情长跑路漫漫,婚姻保险保平安", concreteElementB.GetType().Name, this.GetType().Name); } }
客户端添加:
//客户端添加 Marriage v4 = new Marriage(); o.Display(v4);
时间: 2024-11-05 20:47:05