设计模式之美:Visitor(访问者)
索引
意图
表示一个作用于某对象结构中的各元素的操作。
Visitor 使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
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.
结构
参与者
Visitor
- 为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。该操作的名字和特征标识了发送 Visit 请求给该访问者的那个类。
ConcreteVisitor
- 实现每个由 Visitor 声明的操作。
Element
- 定义一个 Accept 操作,它以一个 Visitor 为参数。
ConcreteElement
- 实现 Accept 操作,该操作以一个 Visitor 为参数。
ObjectStructure
- 能枚举 Element。
- 可以提供一个高层的接口以允许该 Visitor 访问它的元素。
- 可以是一个 Composite 或是一个集合、列表或无序集合。
适用性
在以下情况下可以使用 Visitor 模式:
- 一个对象结构包含很多类操作,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作污染这些对象的类。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
缺点
- 增加新的 ConcreteElement 类很困难。添加新的 ConcreteElement 都要在 Visitor 中添加一个新的抽象操作。
- 可能破坏封装。Visitor 假定 ConcreteElement 接口的功能足够强,足以让 Visitor 进行它的工作。但有时会迫使你提供 ConcreteElement 的内部状态的公共操作。
效果
- Visitor 模式使得易于增加新的操作。
- Visitor 集中相关的操作而分离无关的操作。
相关模式
- Visitor 可以用于对一个 Composite 模式定义的对象结构进行操作。
- Visitor 可以用于 Interpreter 解释。
实现
实现方式(一):Visitor 模式结构样式代码。
namespace VisitorPattern.Implementation1
{
public abstract class Element
{
public abstract void Accept(Visitor visitor);
} public abstract class Visitor
{
public abstract void Visit(ConcreteElementA element);
public abstract void Visit(ConcreteElementB element);
} public class ObjectStructure
{
private List<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 (var element in _elements)
{
element.Accept(visitor);
}
}
} public class ConcreteElementA : Element
{
public string Name { get; set; } public override void Accept(Visitor visitor)
{
visitor.Visit(this);
}
} public class ConcreteElementB : Element
{
public string ID { get; set; } public override void Accept(Visitor visitor)
{
visitor.Visit(this);
}
} public class ConcreteVisitorA : Visitor
{
public override void Visit(ConcreteElementA element)
{
Console.WriteLine(
"ConcreteVisitorA visited ConcreteElementA : {0}",
element.Name);
} public override void Visit(ConcreteElementB element)
{
Console.WriteLine(
"ConcreteVisitorA visited ConcreteElementB : {0}",
element.ID);
}
} public class ConcreteVisitorB : Visitor
{
public override void Visit(ConcreteElementA element)
{
Console.WriteLine(
"ConcreteVisitorB visited ConcreteElementA : {0}",
element.Name);
} public override void Visit(ConcreteElementB element)
{
Console.WriteLine(
"ConcreteVisitorB visited ConcreteElementB : {0}",
element.ID);
}
} public class Client
{
public void TestCase1()
{
var objectStructure = new ObjectStructure(); objectStructure.Attach(new ConcreteElementA());
objectStructure.Attach(new ConcreteElementB()); objectStructure.Accept(new ConcreteVisitorA());
objectStructure.Accept(new ConcreteVisitorB());
}
}
}
实现方式(二):使用 Visitor 模式解构设计。
假设我们有一个 Employee 类,Employee 分为按时薪计算的 Employee 和按月薪计算的 Employee。
public class Employee
{
public abstract string GetHoursAndPayReport();
} public class HourlyEmployee : Employee
{
public override string GetHoursAndPayReport()
{
// generate the line for this hourly employee
return "100 Hours and $1000 in total.";
}
} public class SalariedEmployee : Employee
{
public override string GetHoursAndPayReport()
{
// do nothing
return string.Empty;
}
}
这段代码的问题是,Employee 类及子类耦合了 Salary Report 相关的职责,这侵犯了单一职责原则(Single Responsibility Principle),因为其导致每次需要更改 Report 相关的职责时,都需要修改 Employee 类。
我们是用 Visitor 模式来解决这个问题。
namespace VisitorPattern.Implementation2
{
public abstract class Employee
{
public abstract string Accept(EmployeeVisitor visitor);
} public class HourlyEmployee : Employee
{
public override string Accept(EmployeeVisitor visitor)
{
return visitor.Visit(this);
}
} public class SalariedEmployee : Employee
{
public override string Accept(EmployeeVisitor visitor)
{
return visitor.Visit(this);
}
} public abstract class EmployeeVisitor
{
public abstract string Visit(HourlyEmployee employee);
public abstract string Visit(SalariedEmployee employee);
} public class HoursPayReport : EmployeeVisitor
{
public override string Visit(HourlyEmployee employee)
{
// generate the line of the report.
return "100 Hours and $1000 in total.";
} public override string Visit(SalariedEmployee employee)
{
// do nothing
return string.Empty;
}
}
}
实现方式(三):使用 Acyclic Visitor 模式解构设计。
我们注意到 Employee 类依赖于 EmployeeVisitor 基类。而 EmployeeVisitor 类为每个 Employee 的子类都提供了一个 Visit 方法。
因此,这里形成了一个依赖关系的环。这导致 Visitor 在响应变化时变得复杂。
Visitor 模式在类继承关系不是经常变化时可以工作的很好,但在子类衍生频繁的情况下会增加复杂度。
此时,我们可以应用 Acyclic Visitor 模式,抽象出窄接口,以使 Employee 子类仅依赖于该窄接口。
namespace VisitorPattern.Implementation3
{
public abstract class Employee
{
public abstract string Accept(EmployeeVisitor visitor);
} public class HourlyEmployee : Employee
{
public override string Accept(EmployeeVisitor visitor)
{
try
{
IHourlyEmployeeVisitor hourlyEmployeeVisitor = (IHourlyEmployeeVisitor)visitor;
return hourlyEmployeeVisitor.Visit(this);
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
} return string.Empty;
}
} public class SalariedEmployee : Employee
{
public override string Accept(EmployeeVisitor visitor)
{
try
{
ISalariedEmployeeVisitor salariedEmployeeVisitor = (ISalariedEmployeeVisitor)visitor;
return salariedEmployeeVisitor.Visit(this);
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
} return string.Empty;
}
} public interface IHourlyEmployeeVisitor
{
string Visit(HourlyEmployee employee);
} public interface ISalariedEmployeeVisitor
{
string Visit(SalariedEmployee employee);
} public abstract class EmployeeVisitor
{
} public class HoursPayReport : EmployeeVisitor, IHourlyEmployeeVisitor
{
public string Visit(HourlyEmployee employee)
{
// generate the line of the report.
return "100 Hours and $1000 in total.";
}
} public class SalariedPayReport : EmployeeVisitor, ISalariedEmployeeVisitor
{
public string Visit(SalariedEmployee employee)
{
return "Something";
}
}
}
参考文章
《设计模式之美》为 Dennis Gao 发布于博客园的系列文章,任何未经作者本人同意的人为或爬虫转载均为耍流氓。
设计模式之美:Visitor(访问者)的更多相关文章
- 设计模式23:Visitor 访问者模式(行为型模式)
Visitor 访问者模式(行为型模式) 动机(Motivation)在软件构造过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的修改,将会给子类带来繁重的 ...
- 设计模式学习笔记——Visitor 访问者模式
1.定义IVisitor接口,确定变化所涉及的方法 2.封装变化类.实现IVisitor接口 3.在实体类的变化方法中传入IVisitor接口,由接口确定使用哪一种变化来实现(封装变化) 4.在使用时 ...
- 浅谈设计模式-visitor访问者模式
先看一个和visitor无关的案例.假设你现在有一个书架,这个书架有两种操作,1添加书籍2阅读每一本书籍的简介. //书架public class Bookcase { List<Book> ...
- 设计模式之美:Null Object(空对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...
- 设计模式之美:Extension Object(扩展对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):使用示例结构实现 Extension Object. 实现方式(二):使用泛型实现 IExtensibleObject<T ...
- 设计模式之美:Interpreter(解释器)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Interpreter 模式结构样式代码. 实现方式(二):解释波兰表达式(Polish Notation). 意图 给定一个语 ...
- 设计模式之美:Composite(组合)
索引 意图 结构 参与者 适用性 缺点 效果 相关模式 实现 实现方式(一):在 Component 中定义公共接口以保持透明性但损失安全性. 意图 将对象组合成树形结构以表示 “部分-整体” 的层次 ...
- 《设计模式之美》 <03>面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象 现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程.面向对象和函数式编程.面向对象这种编程风格又是这其中最主流的.现在比较流行的编程语言大部分都是面向对象编程语言.大部分项目也都是 ...
- 设计模式之美:Product Trader(操盘手)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Product Trader 的示例实现. 意图 使客户程序可以通过命名抽象超类和给定规约来创建对象. Product Trad ...
随机推荐
- 搭建高可用mongodb集群(三)—— 深入副本集内部机制
在上一篇文章<搭建高可用mongodb集群(二)—— 副本集> 介绍了副本集的配置,这篇文章深入研究一下副本集的内部机制.还是带着副本集的问题来看吧! 副本集故障转移,主节点是如何选举的? ...
- [转]caffe的配置过程
caffe的配置过程 转:http://blog.csdn.net/brightming/article/details/51106629 版权声明:本文为博主原创文章,欢迎转载!转载请写明原文链 ...
- 4. Prototype(原型)
意图: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性: 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或者 当 ...
- DHCP配置
DHCP服务器IP:192.168.1.10 一,安装dhcp [root@localhost ~]# yum install dhcp Loaded plugins: product-id, sub ...
- c# 导出数据到Excel模板
最近在做一个发邮件的功能,客户要求需要导出一个Excel附件,并给了附件的格式, eg: Last Name 姓 First Name 名 Chinese Characters汉字书写(仅大陆人填写) ...
- 移动端HTML5<video>视频播放优化实践[转]
http://blog.csdn.net/u010918416/article/details/52705732 http://www.xuanfengge.com/html5-video-play. ...
- 使用ycsb测试cassandra
参考 https://github.com/cloudius-systems/osv/wiki/Benchmarking-Cassandra-and-other-NoSQL-databases-wit ...
- Vue组件之自定义表单组件
今天又看了一遍vue的文档,记得之前学习的时候,官方文档中有提过,v-model指令是一个语法糖,做两件事,一个是给表单控件元素绑定value,第二个是当输入时更新绑定的值,不过后来在"表单 ...
- Ubuntu android 开发配置
1. 安装 Java SDK 1.1下载 java-sun-1.7.1.tar.gz 到Downlowd 目录下 1.2 创建java目录 sudo makrdir /usr/java 1.3解压 ...
- serialize data use msgpack
#ifndef _BYTEBUFFER_H #define _BYTEBUFFER_H #include <msgpack.hpp> typedef unsigned char uint8 ...