行为型模式(九) 访问者模式(Visitor)
一、动机(Motivate)
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
二、意图(Intent)
表示一个作用于某对象结构中的各个元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。 ——《设计模式》GoF
三、结构图(Structure)
四、模式的组成
可以看出,在访问者模式的结构图有以下角色:
(1)、抽象访问者角色(Vistor): 声明一个包括多个访问操作,多个操作针对多个具体节点角色(可以说有多少个具体节点角色就有多少访问操作),使得所有具体访问者必须实现的接口。
(2)、具体访问者角色(ConcreteVistor):实现抽象访问者角色中所有声明的接口,也可以说是实现对每个具体节点角色的新的操作。
(3)、抽象节点角色(Element):声明一个接受操作,接受一个访问者对象作为参数,如果有其他参数,可以在这个“接受操作”里在定义相关的参数。
(4)、具体节点角色(ConcreteElement):实现抽象元素所规定的接受操作。
(5)、结构对象角色(ObjectStructure):节点的容器,可以包含多个不同类或接口的容器。
五、访问者模式的代码实现
访问者这个模式在我们现实的编码生活中使用的并不是很多,我就直接贴代码,让大家看代码的结构吧。今天给大家两个代码实例,自己慢慢体会访问者吧。实现代码如下:
- static void Main(string[] args)
- {
- //如果想执行新增加的操作
- ShapeVisitor visitor = new CustomVisitor();
- AppStructure app = new AppStructure(visitor);
- Shape shape = new Rectangle();
- shape.Draw();//执行自己的操作
- app.Process(shape);//执行新的操作
- shape = new Circle();
- shape.Draw();//执行自己的操作
- app.Process(shape);//执行新的操作
- shape = new Line();
- shape.Draw();//执行自己的操作
- app.Process(shape);//执行新的操作
- }
- //抽象图形定义---相当于“抽象节点角色”Element
- public abstract class Shape
- {
- //画图形
- public abstract void Draw();
- //外界注入具体访问者
- public abstract void Accept(ShapeVisitor visitor);
- }
- //抽象访问者 Visitor
- public abstract class ShapeVisitor
- {
- public abstract void Visit(Rectangle shape);
- public abstract void Visit(Circle shape);
- public abstract void Visit(Line shape);
- //这里有一点要说:Visit方法的参数可以写成Shape吗?就是这样 Visit(Shape shape),当然可以,但是ShapeVisitor子类Visit方法就需要判断当前的Shape是什么类型,是Rectangle类型,是Circle类型,或者是Line类型。
- }
- //具体访问者 ConcreteVisitor
- public sealed class CustomVisitor : ShapeVisitor
- {
- //针对Rectangle对象
- public override void Visit(Rectangle shape)
- {
- Console.WriteLine("针对Rectangle新的操作!");
- }
- //针对Circle对象
- public override void Visit(Circle shape)
- {
- Console.WriteLine("针对Circle新的操作!");
- }
- //针对Line对象
- public override void Visit(Line shape)
- {
- Console.WriteLine("针对Line新的操作!");
- }
- }
- //矩形----相当于“具体节点角色” ConcreteElement
- public sealed class Rectangle : Shape
- {
- public override void Draw()
- {
- Console.WriteLine("矩形我已经画好!");
- }
- public override void Accept(ShapeVisitor visitor)
- {
- visitor.Visit(this);
- }
- }
- //圆形---相当于“具体节点角色”ConcreteElement
- public sealed class Circle : Shape
- {
- public override void Draw()
- {
- Console.WriteLine("圆形我已经画好!");
- }
- public override void Accept(ShapeVisitor visitor)
- {
- visitor.Visit(this);
- }
- }
- //直线---相当于“具体节点角色” ConcreteElement
- public sealed class Line : Shape
- {
- public override void Draw()
- {
- Console.WriteLine("直线我已经画好!");
- }
- public override void Accept(ShapeVisitor visitor)
- {
- visitor.Visit(this);
- }
- }
- //结构对象角色
- internal class AppStructure
- {
- private ShapeVisitor _visitor;
- public AppStructure(ShapeVisitor visitor)
- {
- this._visitor = visitor;
- }
- public void Process(Shape shape)
- {
- shape.Accept(_visitor);
- }
- }
这是访问者模式第二种代码实例:
- static void Main(string[] args)
- {
- StoragePlatform platform = new StoragePlatform();
- platform.Attach(new Television());
- platform.Attach(new Computer());
- SizeVisitor sizeVisitor = new SizeVisitor();
- StateVisitor stateVisitor = new StateVisitor();
- platform.Operate(sizeVisitor);
- platform.Operate(stateVisitor);
- }
- //抽象访问者角色 Visitor
- public abstract class Visitor
- {
- public abstract void PutTelevision(Television tv);
- public abstract void PutComputer(Computer comp);
- }
- //具体访问者角色 ConcreteVisitor
- public sealed class SizeVisitor : Visitor
- {
- public override void PutTelevision(Television tv)
- {
- Console.WriteLine("按商品大小{0}排放", tv.Size);
- }
- public override void PutComputer(Computer comp)
- {
- Console.WriteLine("按商品大小{0}排放", comp.Size);
- }
- }
- //具体访问者角色 ConcreteVisitor
- public sealed class StateVisitor : Visitor
- {
- public override void PutTelevision(Television tv)
- {
- Console.WriteLine("按商品新旧值{0}排放", tv.State);
- }
- public override void PutComputer(Computer comp)
- {
- Console.WriteLine("按商品新旧值{0}排放", comp.State);
- }
- }
- //抽象节点角色 Element
- public abstract class Goods
- {
- public abstract void Operate(Visitor visitor);
- private int nSize;
- public int Size
- {
- get { return nSize; }
- set { nSize = value; }
- }
- private int nState;
- public int State
- {
- get { return nState; }
- set { nState = value; }
- }
- }
- //具体节点角色 ConcreteElement
- public sealed class Television : Goods
- {
- public override void Operate(Visitor visitor)
- {
- visitor.PutTelevision(this);
- }
- }
- //具体节点角色 ConcreteElement
- public sealed class Computer : Goods
- {
- public override void Operate(Visitor visitor)
- {
- visitor.PutComputer(this);
- }
- }
- //结构对象角色
- public sealed class StoragePlatform
- {
- private IList<Goods> list = new List<Goods>();
- public void Attach(Goods element)
- {
- list.Add(element);
- }
- public void Detach(Goods element)
- {
- list.Remove(element);
- }
- public void Operate(Visitor visitor)
- {
- foreach (Goods g in list)
- {
- g.Operate(visitor);
- }
- }
- }
六、访问者模式的实现要点:
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的框架类库里面有“访问者模式”实现,看来自己还需努力,革命尚未成功啊。
行为型模式(九) 访问者模式(Visitor)的更多相关文章
- 行为型设计模式之访问者模式(Visitor)
结构 意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 适用性 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依 ...
- 十一个行为模式之访问者模式(Visitor Pattern)
定义: 提供一个作用于某对象结构(通常是一个对象集合)的操作的接口,使得在添加新的操作或者在添加新的元素时,不需要修改原有系统,就可以对各个对象进行操作. 结构图: Visitor:抽象访问者类,对元 ...
- Java设计模式(23)——行为模式之访问者模式(Visitor)
一.概述 概念 作用于某个对象群中各个对象的操作.它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作. 引入 试想这样一个场景,在一个Collection中放入了一大堆的各种对象的引用 ...
- [19/04/30-星期二] GOF23_行为型模式(中介者模式、命令模式、解释器模式、访问者模式)
一.中介者模式(meditor) [中介] /*** * 抽象中介者接口和其具体实现类"经理"类 */ package cn.sxt.meditor; import java.ut ...
- java设计模式9.备忘录模式、访问者模式、调停者模式
备忘录模式 备忘录模式又叫快照模式,备忘录对象是一个用来存储另外一个对象内部状态快照的对象.备忘录的用意是在不破坏封装的条件下,将一个对象的状态捕捉,并外部化存储起来,从而可以在将来合适的时候把这个对 ...
- Head First设计模式——原型模式和访问者模式
原型 原型模式:当创建给定类的过程很昂贵或很复杂时,就使用原型模式. 我们在进行游戏的时候游戏会动态创建怪,而怪时根据场景的不同而变化创建的,英雄自己也会创建一些随从.创建各式各样的怪兽实例,已经越来 ...
- GoF23种设计模式之行为型模式之访问者模式
概述 表示一个作用于某对象结构中的各元素的操作. 它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 适用性 1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依 ...
- 设计模式学习之访问者模式(Visitor,行为型模式)(21)
参考:https://www.cnblogs.com/edisonchou/p/7247990.html 在患者就医时,医生会根据病情开具处方单,很多医院都会存在以下这个流程:划价人员拿到处方单之后根 ...
- 13、Visitor 访问者模式 访问数据结构并处理数据 行为型设计模式
1.模式的定义与特点 访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元 ...
随机推荐
- Theano入门
由于自己的一个小项目需要Theano部分的开源代码,所以学习一下并记录入门的经典网站. 入门中文博客:https://blog.csdn.net/hjimce/article/details/4680 ...
- Clean code 关于注释、函数、命名的感想
最近在看代码整洁之道(Clean code)这本书,其实看的有点痛苦,因为越看就会越想自己写的代码是什么鬼?一些不知所云的命名,不整洁的代码格式,本想诠释代码的意思却添加了一段段废话,还有那些被强制加 ...
- java8新特性六-Optional 类
Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者仅仅保 ...
- git stash详解
应用场景: 1 当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然 ...
- spring boot 初始
前言 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代步遍布的时候,你就行该想到我能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的时间在应用中,而不用担心技术的更新换代.但 ...
- LeetCode | 152. 乘积最大子序列
原题(Medium): 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 思路: 遍历数组时且逐元素相乘时,如果遇到了0,在求乘积最大值的情况下,0左边的元素 ...
- python实战项目 — 爬取 校花网图片
重点: 1. 指定路径创建文件夹,判断是否存在 2. 保存图片文件 # 获得校花网的地址,图片的链接 import re import requests import time import os ...
- 对JAVA工程师绝对有用的Java学习资源清单
学习Java和其他技术的资源其实非常多,但也不是都是好的有用的,我们要取其精华去其糟粕,选择那些最好的,最适合我们的,同时也要由浅入深,先易后难.基于这样的一个标准,我在这里为大家提供一份Java的学 ...
- mysql疑问
- go 读取BMP文件头二进制读取
BMP文件头定义: WORD 两个字节 16bit DWORD 四个字节 32bit package main import ( "encoding/binary" "f ...