封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 换句话说: 访问者模式赋予了【数据】的选择权。 一般而言,我们都是直接通过【数据操作类】操作【数据】。 而通过访问者模式,【数据】可以选择某个【数据操作类】来访问它。
现在的互联网时代真是给我们提供了极大的便利。出门不用带现金了,买票不用本人到火车站了,水电费手机上就缴了,网上购物直邮到家了,吃饭也不用下楼了。 慢着,似乎我要跑题了。 这节可是要讲访问者模式,跟互联网有半毛钱关系。 别急关系是硬扯的。
正如六度空间理论, 又名六度分隔理论。 你至多只要通过六个人就能认识全世界的任意一个人。
这咋一听是不很玄乎。 举个例子,就像你跟隔壁村的老王扯关系一样,最终还是能扯上点亲戚关系的。
下面我们就开始正二八经的扯吧。 我们就以淘宝购物为例来进行访问者模式的思考。
想一想我们在淘宝下单支付之后,淘宝做了什么? 是不是需要捡货发货? 对于拣货员来说,需要根据订单进行拣货。 对于发货员来说,需要根据订单的收货信息,进行快递发货。 .... 就从以上场景来说,针对一张订单,已经有两个不同访问者。 每个访问者访问订单的不同数据,做成不同的操作。
好了,废话不多说,咱们代码见。
假设淘宝后台有一个订单中心,负责订单相关业务的流转。订单一般上而言主要包括两种,销售订单、退货订单。
根据以上购物场景,我们简单抽象出以下几个对象:
客户类主要包含简单的个人信息和收货信息:
- /// <summary>
- /// 客户类
- /// </summary>
- public class Customer {
- public int Id {
- get;
- set;
- }
- public string NickName {
- get;
- set;
- }
- public string RealName {
- get;
- set;
- }
- public string Phone {
- get;
- set;
- }
- public string Address {
- get;
- set;
- }
- public string Zip {
- get;
- set;
- }
- }
产品类简单包含产品名称、价格信息:
- /// <summary>
- /// 产品类
- /// </summary>
- public class Product {
- public int Id {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public virtual decimal Price {
- get;
- set;
- }
- }
下面来看看订单相关类:
- /// <summary>
- /// 订单抽象类
- /// </summary>
- public abstract class Order {
- public int Id {
- get;
- set;
- }
- public Customer Customer {
- get;
- set;
- }
- public DateTime CreatorDate {
- get;
- set;
- }
- /// <summary>
- /// 单据品项
- /// </summary>
- public List < OrderLine > OrderItems {
- get;
- set;
- }
- public abstract void Accept(Visitor visitor);
- }
- /// <summary>
- /// 销售订单
- /// </summary>
- public class SaleOrder: Order {
- public override void Accept(Visitor visitor) {
- visitor.Visit(this);
- }
- }
- /// <summary>
- /// 退货单
- /// </summary>
- public class ReturnOrder: Order {
- public override void Accept(Visitor visitor) {
- visitor.Visit(this);
- }
- }
- public class OrderLine {
- public int Id {
- get;
- set;
- }
- public Product Product {
- get;
- set;
- }
- public int Qty {
- get;
- set;
- }
- }
其中
类定义了一个抽象方法
- Order
,子类通过
- Accept(Visitor visitor);
直接简单重载。
- visitor.Visit(this)
下面我们来看下访问者角色的定义:
- /// <summary>
- /// 访问者
- /// </summary>
- public abstract class Visitor
- {
- public abstract void Visit(SaleOrder saleOrder);
- public abstract void Visit(ReturnOrder returnOrder);
- }
其中主要定义了两个抽象
方法,用来分别对
- Visit
和
- SaleOrder
进行处理。
- ReturnOrder
接下来我们就来看看具体的访问者的实现吧:
- /// <summary>
- /// 捡货员
- /// 对销售订单,从仓库捡货。
- /// 对退货订单,将收到的货品归放回仓库。
- /// </summary>
- public class Picker: Visitor {
- public int Id {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public override void Visit(SaleOrder saleOrder) {
- Console.WriteLine($ "开始为销售订单【{saleOrder.Id}】进行销售捡货处理:");
- foreach(var item in saleOrder.OrderItems) {
- Console.WriteLine($ "【{item.Product.Name}】商品* {item.Qty}");
- }
- Console.WriteLine($ "订单【{saleOrder.Id}】捡货完毕!");
- Console.WriteLine("==========================");
- }
- public override void Visit(ReturnOrder returnOrder) {
- Console.WriteLine($ "开始为退货订单【{returnOrder.Id}】进行退货捡货处理:");
- foreach(var item in returnOrder.OrderItems) {
- Console.WriteLine($ "【{item.Product.Name}】商品* {item.Qty}");
- }
- Console.WriteLine($ "退货订单【{returnOrder.Id}】退货捡货完毕!", returnOrder.Id);
- Console.WriteLine("==========================");
- }
- }
- /// <summary>
- /// 收发货员
- /// 对销售订单,进行发货处理
- /// 对退货订单,进行收货处理
- /// </summary>
- public class Distributor: Visitor {
- public int Id {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public override void Visit(SaleOrder saleOrder) {
- Console.WriteLine($ "开始为销售订单【{saleOrder.Id}】进行发货处理:", saleOrder.Id);
- Console.WriteLine($ "一共打包{saleOrder.OrderItems.Sum(line => line.Qty)}件商品。");
- Console.WriteLine($ "收货人:{saleOrder.Customer.RealName}");
- Console.WriteLine($ "联系电话:{saleOrder.Customer.Phone}");
- Console.WriteLine($ "收货地址:{saleOrder.Customer.Address}");
- Console.WriteLine($ "邮政编码:{saleOrder.Customer.Zip}");
- Console.WriteLine($ "订单【{saleOrder.Id}】发货完毕!");
- Console.WriteLine("==========================");
- }
- public override void Visit(ReturnOrder returnOrder) {
- Console.WriteLine($ "收到来自【{returnOrder.Customer.NickName}】的退货订单【{returnOrder.Id}】,进行退货收货处理:");
- foreach(var item in returnOrder.OrderItems) {
- Console.WriteLine($ "【{item.Product.Name}】商品* {item.Qty}");
- }
- Console.WriteLine($ "退货订单【{returnOrder.Id}】收货处理完毕!");
- Console.WriteLine("==========================");
- }
- }
代码中已经写的够清楚了,我就不多说了。
最后上下我们的订单中心的代码:
- /// <summary>
- /// 订单中心
- /// </summary>
- public class OrderCenter : List<Order>
- {
- public void Accept(Visitor visitor)
- {
- var iterator = this.GetEnumerator();
- while (iterator.MoveNext())
- {
- iterator.Current.Accept(visitor);
- }
- }
- }
就是简单的集合类,提供了一个
- OrderCenter
方法来指定接受哪一种访问者访问。
- Accept(Visitor visitor)
看看场景类:
- static void Main(string[] args) {
- Customer customer = new Customer {
- Id = 1,
- NickName = "圣杰",
- RealName = "圣杰",
- Address = "深圳市南山区",
- Phone = "135****9358",
- Zip = "518000"
- };
- Product productA = new Product {
- Id = 1,
- Name = "小米5",
- Price = 1899
- };
- Product productB = new Product {
- Id = 2,
- Name = "小米5手机防爆膜",
- Price = 29
- };
- Product productC = new Product {
- Id = 3,
- Name = "小米5手机保护套",
- Price = 69
- };
- OrderLine line1 = new OrderLine {
- Id = 1,
- Product = productA,
- Qty = 1
- };
- OrderLine line2 = new OrderLine {
- Id = 1,
- Product = productB,
- Qty = 2
- };
- OrderLine line3 = new OrderLine {
- Id = 1,
- Product = productC,
- Qty = 3
- };
- //先买了个小米5和防爆膜
- SaleOrder order1 = new SaleOrder {
- Id = 1,
- Customer = customer,
- CreatorDate = DateTime.Now,
- OrderItems = new List < OrderLine > {
- line1,
- line2
- }
- };
- //又买了个保护套
- SaleOrder order2 = new SaleOrder {
- Id = 2,
- Customer = customer,
- CreatorDate = DateTime.Now,
- OrderItems = new List < OrderLine > {
- line3
- }
- };
- //把保护套都退了
- ReturnOrder returnOrder = new ReturnOrder {
- Id = 3,
- Customer = customer,
- CreatorDate = DateTime.Now,
- OrderItems = new List < OrderLine > {
- line3
- }
- };
- OrderCenter orderCenter = new OrderCenter {
- order1,
- order2,
- returnOrder
- };
- Picker picker = new Picker {
- Id = 110,
- Name = "捡货员110"
- };
- Distributor distributor = new Distributor {
- Id = 111,
- Name = "发货货员111"
- };
- //捡货员访问订单中心
- orderCenter.Accept(picker);
- //发货员访问订单中心
- orderCenter.Accept(distributor);
- Console.ReadLine();
- }
从上例我们结合访问者模式的通用类图,来理一理主要的几个角色:
和
- Picker
。我们在捡货员和发货员分别定义了处理销售订单和退货订单的行为。
- Distributor
方法,由子类指定接受哪一种访问者访问。例子中就是我们的
- Accept
类。
- Order
实现父类定义的抽象
- visitor.Visit(this)
方法。例子中,
- Accept
和
- SaleOrder
就是这样做的。
- ReturnOrder
维护的一个
- OrderCenter
集合。
- Order
源代码 C# GitHub
设计模式之小试牛刀
来源: http://www.cnblogs.com/sheng-jie/p/6724311.html