C#设计模式之二十一访问者模式(Visitor Pattern)【行为型】

简介: 原文:C#设计模式之二十一访问者模式(Visitor Pattern)【行为型】一、引言   今天我们开始讲“行为型”设计模式的第九个模式,该模式是【访问者模式】,英文名称是:Visitor Pattern。
原文: C#设计模式之二十一访问者模式(Visitor Pattern)【行为型】

一、引言

   今天我们开始讲“行为型”设计模式的第九个模式,该模式是【访问者模式】,英文名称是:Visitor Pattern。如果按老规矩,先从名称上来看看这个模式,我根本不能获得任何对理解该模式有用的信息,而且这个模式在我们的编码生活中使用的并不是很多。该模式的意图定义很抽象,第一次看了这个定义其实和没看没有什么区别,一头雾水,为了让大家更好的理解该模式的初衷,我们举个例子来说明模式。比如:当我们为了解决一个新的软件需求的时候,经过多个日以继夜的努力,最终通过一个完美(自己认为的)的软件设计解决了客户提出的新的需求,而且这个设计有完美的类层次结构,并且是符合OO的设计原则的,我们很开心,对自己设计的东西很有成就感。又过了一段时间,客户突然又有了一个新的需求,需要为现有的类层次结构里面的类增加一个新的操作(其实就是一个方法),怎么办?好办,在面向OO设计模式中有一个模式就是为了解决这个问题的,那就是“访问者模式”,可以为现有的类层次结构中的类轻松增加新的操作,我们继续吧,好好的了解一下该模式。

二、访问者模式的详细介绍

2.1、动机(Motivate)

   在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

2.2、意图(Intent)

   表示一个作用于某对象结构中的各个元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。                                      ——《设计模式》GoF

2.3、结构图(Structure)

    

2.4、模式的组成
    
    可以看出,在访问者模式的结构图有以下角色:

    (1)、抽象访问者角色(Vistor): 声明一个包括多个访问操作,多个操作针对多个具体节点角色(可以说有多少个具体节点角色就有多少访问操作),使得所有具体访问者必须实现的接口。

    (2)、具体访问者角色(ConcreteVistor):实现抽象访问者角色中所有声明的接口,也可以说是实现对每个具体节点角色的新的操作。

    (3)、抽象节点角色(Element):声明一个接受操作,接受一个访问者对象作为参数,如果有其他参数,可以在这个“接受操作”里在定义相关的参数。

    (4)、具体节点角色(ConcreteElement):实现抽象元素所规定的接受操作。

    (5)、结构对象角色(ObjectStructure):节点的容器,可以包含多个不同类或接口的容器。

2.5、访问者模式的代码实现

    访问者这个模式在我们现实的编码生活中使用的并不是很多,我就直接贴代码,让大家看代码的结构吧。今天给大家两个代码实例,自己慢慢体会访问者吧。实现代码如下:

  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 }


三、访问者模式的实现要点:

    Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。

  设计模式其实是一种堵漏洞的方式,但是没有一种设计模式能够堵完所有的漏洞,即使是组合各种设计模式也是一样。每个设计模式都有漏洞,都有它们解决不了的情况或者变化。每一种设计模式都假定了某种变化,也假定了某种不变化。Visitor模式假定的就是操作变化,而Element类层次结构稳定。

    (1)、访问者模式的主要优点有:

        1】、访问者模式使得添加新的操作变得容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,添加新的操作会变得很复杂。而使用访问者模式,增加新的操作就意味着添加一个新的访问者类。因此,使得添加新的操作变得容易。

        2】、访问者模式使得有关的行为操作集中到一个访问者对象中,而不是分散到一个个的元素类中。这点类似与”中介者模式”。

        3】、访问者模式可以访问属于不同的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。

 (2)、访问者模式的主要缺点有:

        1】、增加新的元素类变得困难。每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体操作。具体来说,Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。

    (3)、在下面的情况下可以考虑使用访问者模式:

        1】、如果系统有比较稳定的数据结构,而又有易于变化的算法时,此时可以考虑使用访问者模式。因为访问者模式使得算法操作的添加比较容易。

        2】、如果一组类中,存在着相似的操作,为了避免出现大量重复的代码,可以考虑把重复的操作封装到访问者中。(当然也可以考虑使用抽象类了)

        3】、如果一个对象存在着一些与本身对象不相干,或关系比较弱的操作时,为了避免操作污染这个对象,则可以考虑把这些操作封装到访问者对象中。


四、.NET 访问者模式的实现

     在现在的Net框架里面,如果要想给现有的类增加新的方法,有了新的方式,那就是“扩展方法”,使用起来和实例方法是一样一样的,而且在Net框架里面,微软自己也写了很多的扩展方法给我们使用。我目前还没有学习到Net的框架类库里面有“访问者模式”实现,看来自己还需努力,革命尚未成功啊。

五、总结

    访问者模式写完了,这个模式刚开始理解起来还是挺麻烦的,但是,如果我们多看几个实例代码,完全掌握也不是问题。随着C#语言的发展,设计模式里面的很多东西,我们可以通过C#语言的一些特性做更好的替代。我们写设计模式刚开始要慢慢来,一步一步的照猫画虎的来写代码,等我们熟练掌握了模式的核心意思,我们就要写符合C#风格和特性的模式代码了,或者说我们要用C#来写设计模式了,写出来的代码会更棒。

目录
相关文章
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
30 3
|
2月前
|
设计模式
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
|
6月前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
|
2月前
|
设计模式 Java
设计模式--适配器模式 Adapter Pattern
这篇文章介绍了适配器模式,包括其基本介绍、工作原理以及类适配器模式、对象适配器模式和接口适配器模式三种实现方式。
|
2月前
|
设计模式 缓存 算法
Java设计模式-访问者模式(22)
Java设计模式-访问者模式(22)
|
5月前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)
|
6月前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
|
6月前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
42 2
|
6月前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
49 1
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~访问者模式
[设计模式 Go实现] 行为型~访问者模式

热门文章

最新文章

  • 1
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    45
  • 2
    C++一分钟之-C++中的设计模式:单例模式
    51
  • 3
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    36
  • 4
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    59
  • 5
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    54
  • 6
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    39
  • 7
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 8
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    102
  • 9
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    74
  • 10
    Go语言设计模式:使用Option模式简化类的初始化
    71