模式动机
对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同,访问者模式为解决这类问题而诞生。
在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式。
而且这些操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求。
此时,访问者模式就是一个值得考虑的解决方案。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。
模式定义
访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
Visitor Pattern: Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
Frequency of use: low
UML图
模式结构
访问者模式包含如下角色:
Vistor: 抽象访问者
ConcreteVisitor: 具体访问者
Element: 抽象元素
ConcreteElement: 具体元素
ObjectStructure: 对象结构
模式分析
访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。
访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。
相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。
模式实例与解析
男人和女人—访问者模式示例代码
体系结构
Vistor: 抽象访问者 Action.cs
namespace VisitorPattern { abstract class Action { //得到男人结论或女人反应 public abstract void GetManConclusion(Man concreateElementA); //得到女人结论或男人反应 public abstract void GetWomanConclusion(Woman concreateElementB); } }
ConcreteVisitor: 具体访问者 Success.cs
using System; namespace VisitorPattern { //成功 class Success : Action { public override void GetManConclusion(Man concreateElementA) { Console.WriteLine("{0}{1}时,背后多半有一个伟大的女人。", concreateElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreateElementB) { Console.WriteLine("{0}{1}时,背后大多有一个不成功的男人。", concreateElementB.GetType().Name, this.GetType().Name); } } }
Failing.cs
using System; namespace VisitorPattern { class Failing : Action { public override void GetManConclusion(Man concreateElementA) { Console.WriteLine("{0}{1}时,闷头喝酒,谁也不用劝。", concreateElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreateElementB) { Console.WriteLine("{0}{1}时,眼泪汪汪,谁也劝不了。", concreateElementB.GetType().Name, this.GetType().Name); } } }
Amativenness.cs
using System; namespace VisitorPattern { class Amativenness : Action { public override void GetManConclusion(Man concreateElementA) { Console.WriteLine("{0}{1}时,凡事不懂也要装懂。", concreateElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreateElementB) { Console.WriteLine("{0}{1}时,遇事懂也装作不懂。", concreateElementB.GetType().Name, this.GetType().Name); } } }
Element: 抽象元素 Person.cs
namespace VisitorPattern { abstract class Person { //接受 public abstract void Accept(Action visitor); } }
ConcreteElement: 具体元素 Man.cs
using System; namespace VisitorPattern { class Man : Person { public override void Accept(Action visitor) { visitor.GetManConclusion(this); } } }
Woman.cs
using System; namespace VisitorPattern { class Woman : Person { public override void Accept(Action visitor) { visitor.GetWomanConclusion(this); } } }
ObjectStructure: 对象结构 ObjectStructure.cs
using System.Collections.Generic; namespace VisitorPattern { //对象结构 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 e in elements) { e.Accept(visitor); } } } }
Client:客户类
using System; namespace VisitorPattern { 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); //恋爱时的反应 Amativenness v3 = new Amativenness(); o.Display(v3); Console.Read(); } } }
模式优缺点
访问者模式的优点
使得增加新的访问操作变得很容易。
将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。
可以跨过类的等级结构访问属于不同的等级结构的元素类。
让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。
访问者模式的缺点
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
模式适用环境
在以下情况下可以使用访问者模式:
一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
【声明与感谢】
本文,站在许多巨人的肩膀上,借鉴和引用了许多他人拥有版权的作品或著述,在此,对前人们的贡献致谢。并同时公布引用的内容、原作者或来源(一些来源于互联网的内容本人无法追述本源,深表遗憾)。
【参考文献】
《设计模式—可复用面向对象软件的基础》作者: [美] Erich Gamma / Richard Helm / Ralph Johnson / John Vlissides 译者: 李英军 / 马晓星 / 蔡敏 / 刘建中 等 机械工业出版社
《重构—改善既有代码的设计》作者: Martin Fowler译者:候捷 中国电力出版社
《敏捷软件开发—原则、模式与实践》作者: Robert C. Martin 清华大学出版社
《程序员修炼之道—从小工到专家》作者: Andrew Hunt / David Thomas 电子工业出版社
《Head First 设计模式》作者: 弗里曼 译者: O'Reilly Taiwan公司 中国电力出版社
《设计模式之禅》 作者: 秦小波 机械工业出版社
MSDN WebCast 《C#面向对象设计模式纵横谈》 讲师:李建忠
刘伟. 设计模式. 北京:清华大学出版社, 2011.
刘伟. 设计模式实训教程. 北京:清华大学出版社, 2012.
《大话设计模式》 作者: 程杰 清华大学出版社
《C#图解教程》作者: 索利斯 译者: 苏林 / 朱晔 人民邮电出版社
《你必须知道的.NET》作者: 王涛
《项目中的.NET》作者: 李天平 电子工业出版社
《Microsoft .NET企业级应用架构设计》作者: (美)埃斯波西托等编著 译者: 陈黎夫
http://www.dofactory.com/Patterns/Patterns.aspx .NET Design Patterns
http://www.cnblogs.com/zhenyulu 博客作者:吕震宇
http://www.cnblogs.com/terrylee 博客作者:李会军
http://www.cnblogs.com/anlyren/ 博客作者:anlyren
http://www.cnblogs.com/idior 博客作者:idior
http://www.cnblogs.com/allenlooplee 博客作者:Allen lee
http://blog.csdn.net/ai92 博客作者:ai92
http://www.cnblogs.com/umlonline/ 博客作者:张传波
http://www.cnblogs.com/lovecherry/ 博客作者:LoveCherry