1、引言
今天咱们开始讲“行为型”设计模式的第九个模式,该模式是【访问者模式】,英文名称是:Visitor Pattern。若是按老规矩,先从名称上来看看这个模式,我根本不能得到任何对理解该模式有用的信息,并且这个模式在咱们的编码生活中使用的并非不少。该模式的意图定义很抽象,第一次看了这个定义其实和没看没有什么区别,一头雾水,为了让你们更好的理解该模式的初衷,咱们举个例子来讲明模式。好比:当咱们为了解决一个新的软件需求的时候,通过多个日以继夜的努力,最终经过一个完美(本身认为的)的软件设计解决了客户提出的新的需求,并且这个设计有完美的类层次结构,而且是符合OO的设计原则的,咱们很开心,对本身设计的东西颇有成就感。又过了一段时间,客户忽然又有了一个新的需求,须要为现有的类层次结构里面的类增长一个新的操做(其实就是一个方法),怎么办?好办,在面向OO设计模式中有一个模式就是为了解决这个问题的,那就是“访问者模式”,能够为现有的类层次结构中的类轻松增长新的操做,咱们继续吧,好好的了解一下该模式。
2、访问者模式的详细介绍
2.一、动机(Motivate)
在软件构建过程当中,因为需求的改变,某些类层次结构中经常须要增长新的行为(方法),若是直接在基类中作这样的更改,将会给子类带来很繁重的变动负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据须要透明地为类层次结构上的各个类动态添加新的操做,从而避免上述问题?
2.二、意图(Intent)
表示一个做用于某对象结构中的各个元素的操做。它能够在不改变各元素的类的前提下定义做用于这些元素的新的操做。 ——《设计模式》GoF
2.三、结构图(Structure)
2.四、模式的组成
能够看出,在访问者模式的结构图有如下角色:
(1)、抽象访问者角色(Vistor): 声明一个包括多个访问操做,多个操做针对多个具体节点角色(能够说有多少个具体节点角色就有多少访问操做),使得全部具体访问者必须实现的接口。
(2)、具体访问者角色(ConcreteVistor):实现抽象访问者角色中全部声明的接口,也能够说是实现对每一个具体节点角色的新的操做。
(3)、抽象节点角色(Element):声明一个接受操做,接受一个访问者对象做为参数,若是有其余参数,能够在这个“接受操做”里在定义相关的参数。
(4)、具体节点角色(ConcreteElement):实现抽象元素所规定的接受操做。
(5)、结构对象角色(ObjectStructure):节点的容器,能够包含多个不一样类或接口的容器。
2.五、访问者模式的代码实现
访问者这个模式在咱们现实的编码生活中使用的并非不少,我就直接贴代码,让你们看代码的结构吧。今天给你们两个代码实例,本身慢慢体会访问者吧。实现代码以下:html
1 namespace Vistor 2 { 3 //抽象图形定义---至关于“抽象节点角色”Element 4 public abstract class Shape 5 { 6 //画图形 7 public abstract void Draw(); 8 //外界注入具体访问者 9 public abstract void Accept(ShapeVisitor visitor); 10 } 11 12 //抽象访问者 Visitor 13 public abstract class ShapeVisitor 14 { 15 public abstract void Visit(Rectangle shape); 16 17 public abstract void Visit(Circle shape); 18 19 public abstract void Visit(Line shape); 20 21 //这里有一点要说:Visit方法的参数能够写成Shape吗?就是这样 Visit(Shape shape),固然能够,可是ShapeVisitor子类Visit方法就须要判断当前的Shape是什么类型,是Rectangle类型,是Circle类型,或者是Line类型。 22 } 23 24 //具体访问者 ConcreteVisitor 25 public sealed class CustomVisitor : ShapeVisitor 26 { 27 //针对Rectangle对象 28 public override void Visit(Rectangle shape) 29 { 30 Console.WriteLine("针对Rectangle新的操做!"); 31 } 32 //针对Circle对象 33 public override void Visit(Circle shape) 34 { 35 Console.WriteLine("针对Circle新的操做!"); 36 } 37 //针对Line对象 38 public override void Visit(Line shape) 39 { 40 Console.WriteLine("针对Line新的操做!"); 41 } 42 } 43 44 //矩形----至关于“具体节点角色” ConcreteElement 45 public sealed class Rectangle : Shape 46 { 47 public override void Draw() 48 { 49 Console.WriteLine("矩形我已经画好!"); 50 } 51 52 public override void Accept(ShapeVisitor visitor) 53 { 54 visitor.Visit(this); 55 } 56 } 57 58 //圆形---至关于“具体节点角色”ConcreteElement 59 public sealed class Circle : Shape 60 { 61 public override void Draw() 62 { 63 Console.WriteLine("圆形我已经画好!"); 64 } 65 66 public override void Accept(ShapeVisitor visitor) 67 { 68 visitor.Visit(this); 69 } 70 } 71 72 //直线---至关于“具体节点角色” ConcreteElement 73 public sealed class Line : Shape 74 { 75 public override void Draw() 76 { 77 Console.WriteLine("直线我已经画好!"); 78 } 79 80 public override void Accept(ShapeVisitor visitor) 81 { 82 visitor.Visit(this); 83 } 84 } 85 86 //结构对象角色 87 internal class AppStructure 88 { 89 private ShapeVisitor _visitor; 90 91 public AppStructure(ShapeVisitor visitor) 92 { 93 this._visitor = visitor; 94 } 95 96 public void Process(Shape shape) 97 { 98 shape.Accept(_visitor); 99 } 100 } 101 102 class Program 103 { 104 static void Main(string[] args) 105 { 106 //若是想执行新增长的操做 107 ShapeVisitor visitor = new CustomVisitor(); 108 AppStructure app = new AppStructure(visitor); 109 110 Shape shape = new Rectangle(); 111 shape.Draw();//执行本身的操做 112 app.Process(shape);//执行新的操做 113 114 115 shape = new Circle(); 116 shape.Draw();//执行本身的操做 117 app.Process(shape);//执行新的操做 118 119 120 shape = new Line(); 121 shape.Draw();//执行本身的操做 122 app.Process(shape);//执行新的操做 123 124 125 Console.ReadLine(); 126 } 127 } 128 }
这是访问者模式第二种代码实例:算法
1 namespace Visitor 2 { 3 //抽象访问者角色 Visitor 4 public abstract class Visitor 5 { 6 public abstract void PutTelevision(Television tv); 7 8 public abstract void PutComputer(Computer comp); 9 } 10 11 //具体访问者角色 ConcreteVisitor 12 public sealed class SizeVisitor : Visitor 13 { 14 public override void PutTelevision(Television tv) 15 { 16 Console.WriteLine("按商品大小{0}排放", tv.Size); 17 } 18 19 public override void PutComputer(Computer comp) 20 { 21 Console.WriteLine("按商品大小{0}排放", comp.Size); 22 } 23 } 24 25 //具体访问者角色 ConcreteVisitor 26 public sealed class StateVisitor : Visitor 27 { 28 public override void PutTelevision(Television tv) 29 { 30 Console.WriteLine("按商品新旧值{0}排放", tv.State); 31 } 32 33 public override void PutComputer(Computer comp) 34 { 35 Console.WriteLine("按商品新旧值{0}排放", comp.State); 36 } 37 } 38 39 //抽象节点角色 Element 40 public abstract class Goods 41 { 42 public abstract void Operate(Visitor visitor); 43 44 private int nSize; 45 public int Size 46 { 47 get { return nSize; } 48 set { nSize = value; } 49 } 50 51 private int nState; 52 public int State 53 { 54 get { return nState; } 55 set { nState = value; } 56 } 57 } 58 59 //具体节点角色 ConcreteElement 60 public sealed class Television : Goods 61 { 62 public override void Operate(Visitor visitor) 63 { 64 visitor.PutTelevision(this); 65 } 66 } 67 68 //具体节点角色 ConcreteElement 69 public sealed class Computer : Goods 70 { 71 public override void Operate(Visitor visitor) 72 { 73 visitor.PutComputer(this); 74 } 75 } 76 77 //结构对象角色 78 public sealed class StoragePlatform 79 { 80 private IList<Goods> list = new List<Goods>(); 81 82 public void Attach(Goods element) 83 { 84 list.Add(element); 85 } 86 87 public void Detach(Goods element) 88 { 89 list.Remove(element); 90 } 91 92 public void Operate(Visitor visitor) 93 { 94 foreach (Goods g in list) 95 { 96 g.Operate(visitor); 97 } 98 } 99 } 100 101 class Program 102 { 103 static void Main(string[] args) 104 { 105 StoragePlatform platform = new StoragePlatform(); 106 platform.Attach(new Television()); 107 platform.Attach(new Computer()); 108 109 SizeVisitor sizeVisitor = new SizeVisitor(); 110 StateVisitor stateVisitor = new StateVisitor(); 111 112 platform.Operate(sizeVisitor); 113 platform.Operate(stateVisitor); 114 115 Console.Read(); 116 } 117 } 118 }
3、访问者模式的实现要点:
Visitor模式经过所谓双重分发(double dispatch)来实如今不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操做。所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。
设计模式实际上是一种堵漏洞的方式,可是没有一种设计模式可以堵完全部的漏洞,即便是组合各类设计模式也是同样。每一个设计模式都有漏洞,都有它们解决不了的状况或者变化。每一种设计模式都假定了某种变化,也假定了某种不变化。Visitor模式假定的就是操做变化,而Element类层次结构稳定。
(1)、访问者模式的主要优势有:
1】、访问者模式使得添加新的操做变得容易。若是一些操做依赖于一个复杂的结构对象的话,那么通常而言,添加新的操做会变得很复杂。而使用访问者模式,增长新的操做就意味着添加一个新的访问者类。所以,使得添加新的操做变得容易。
2】、访问者模式使得有关的行为操做集中到一个访问者对象中,而不是分散到一个个的元素类中。这点相似与”中介者模式”。
3】、访问者模式能够访问属于不一样的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。
(2)、访问者模式的主要缺点有:
1】、增长新的元素类变得困难。每增长一个新的元素意味着要在抽象访问者角色中增长一个新的抽象操做,并在每个具体访问者类中添加相应的具体操做。具体来讲,Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会致使Visitor类的改变。所以Visitor模式适用于“Element类层次结构稳定,而其中的操做却常常面临频繁改动”。
(3)、在下面的状况下能够考虑使用访问者模式:
1】、若是系统有比较稳定的数据结构,而又有易于变化的算法时,此时能够考虑使用访问者模式。由于访问者模式使得算法操做的添加比较容易。
2】、若是一组类中,存在着类似的操做,为了不出现大量重复的代码,能够考虑把重复的操做封装到访问者中。(固然也能够考虑使用抽象类了)
3】、若是一个对象存在着一些与自己对象不相干,或关系比较弱的操做时,为了不操做污染这个对象,则能够考虑把这些操做封装到访问者对象中。
4、.NET 访问者模式的实现
在如今的Net框架里面,若是要想给现有的类增长新的方法,有了新的方式,那就是“扩展方法”,使用起来和实例方法是同样同样的,并且在Net框架里面,微软本身也写了不少的扩展方法给咱们使用。我目前尚未学习到Net的框架类库里面有“访问者模式”实现,看来本身还需努力,革命还没有成功啊。
5、总结
访问者模式写完了,这个模式刚开始理解起来仍是挺麻烦的,可是,若是咱们多看几个实例代码,彻底掌握也不是问题。随着C#语言的发展,设计模式里面的不少东西,咱们能够经过C#语言的一些特性作更好的替代。咱们写设计模式刚开始要慢慢来,一步一步的照猫画虎的来写代码,等咱们熟练掌握了模式的核心意思,咱们就要写符合C#风格和特性的模式代码了,或者说咱们要用C#来写设计模式了,写出来的代码会更棒。设计模式