当前位置: 首页 > news >正文

那些开发中用到的模式——访问者模式

为了尽可能演示出Visitor 设计模式的强大之处,在此举一个开发中的场景
例如 开发A组 负责做log功能,而B组需要A组暴露一个API,可以拿到所有的log。


A组的代码实现可能是如下这样的:


public abstract class OrderLog{
public string Content {get;set;}
public OrderLog(string content){
Content = content;
}


}


public class PlaceOrderLog :OrderLog{
public PlaceOrderLog(string content,string orderedBy):base(content)
{
OrderedBy = orderedBy;
}
public string OrderedBy {get;set;}


}


public class MakePaymentLog :OrderLog{
public MakePaymentLog(string content, string paymentGateway, string payedBy):base(content)
{
PaymentGateway = paymentGateway;
PayedBy = payedBy;
}
public string PaymentGateway {get;set;}
public string PayedBy {get;set;}


}


public class OrderCompleteLog : OrderLog{
public OrderCompleteLog(string content, DateTime completeDate):base(content)
{
OrderCompleteDate = completeDate;
}
public DateTime OrderCompleteDate {get;set;}


}


public class OrderLogger{
public static OrderLogger Do
{
get
{
return new OrderLogger();
}
}


public IEnumerable<OrderLog> GetLogs(){
return new List<OrderLog>{
new PlaceOrderLog("place order log","ordered by"),
new MakePaymentLog("make payment log","paypal", "payedBy"),
new OrderCompleteLog("order complete log",DateTime.Now)
};


}


}



B组用法:
var logs = OrderLogger.Do.GetLogs();




B组新需求来了,需要A组再做一个API,可以把颜色信息加入LOG实体中,比如类型A返回红色,类型B返回绿色
A组有两个选择,再加一个API,还是把这个责任分发回B组,因为显然显示颜色的逻辑B组更熟悉。当然是分发责任,可如何分发,访问者模式来了。


1.在Log基类中添加一行:
public abstract T Visit<T>(ILogVisitor<T> visitor);




2.其他log子类中相应的实现它:
public override T Visit<T>(ILogVisitor<T> visitor){
return visitor.Visit(this);
}


为什么加这个方法?相当于给外界开一个入口,这样它们可以进来,然后把自身的信息传递给它们完成相应逻辑。


3.给B组一个接口(如果没有返回值,可以把T拿掉):
public interface ILogVisitor<T>{
T Visit(PlaceOrderLog log);
T Visit(MakePaymentLog log);
T Visit(OrderCompleteLog log);
}



为什么加这个接口?
1. 外部的访问者实现这个接口,就可以获取每个子类
2. 如果类层次变化,外部可以知道
3. 外部调用不用写大量的switch-case 或 if else臃肿的判断逻辑了


A组工作完成。接下来是B组要做的事情了——实现访问者接口。
实现也许是这样的:


public enum OrderLogColor{Red,Green}


public class OrderLogColorVisitor:ILogVisitor<OrderLogColor>{
public OrderLogColor Visit(PlaceOrderLog log){
return OrderLogColor.Red;
}


public OrderLogColor Visit(MakePaymentLog log){
return OrderLogColor.Green;
}


public OrderLogColor Visit(OrderCompleteLog log){
return OrderLogColor.Green;
}


}




逻辑很简单,对于不同的类型返回不同颜色。接下来就是调用部分:




var logs = OrderLogger.Do.GetLogs();
	show colors
	var colorVisitor = new OrderLogColorVisitor();
	foreach(var log in logs){
	Console.WriteLine(log.Visit<OrderLogColor>(colorVisitor));
	}




可以看到,拿到一个日志集合后,循环过程只需调用Visit函数,就进入了相应的log类,然后在log类中,调用Visit函数完成Visit逻辑。执行结果:
Red
Green
Green




接下来,B组需要一个新的任务,对不同的日志类型,格式化显示不同的字符串。


于是只需要添加另一个Visitor:
public class OrderLogFormattedVisitor : ILogVisitor <string> {


public string Visit(PlaceOrderLog log){
return "this is place order log formatted information ";
}


public string Visit(MakePaymentLog log){
return "this is make payment log formatted information ";
}


public string Visit(OrderCompleteLog log){
return "this is order complete log formatted information ";
}


}



调用:
var logs = OrderLogger.Do.GetLogs();
var formatVisitor = new OrderLogFormattedVisitor();
	foreach(var log in logs){
	Console.WriteLine(log.Visit<string>(formatVisitor));
	}




看出访问者模式的作用了吗?它把职责隔离的同时,有效的封装了变化。在以上例子中,A组只负责做LOG API,而B组负责拿到LOG后的事情。 在开发中会经常遇到相互调用的情况,每当这个时候,首先考虑职责分配,用接口隔离工作,决定调用的入口。这便是访问者模式的强大之处。


以下是完整的代码:


void Main()
{
	var logs = OrderLogger.Do.GetLogs();
	show colors
	var colorVisitor = new OrderLogColorVisitor();
	foreach(var log in logs){
	Console.WriteLine(log.Visit<OrderLogColor>(colorVisitor));
	}
	show formatted logs
	var formatVisitor = new OrderLogFormattedVisitor();
	foreach(var log in logs){
	Console.WriteLine(log.Visit<string>(formatVisitor));
	}
}


team A job
public abstract class OrderLog{
public string Content {get;set;}
public OrderLog(string content){
Content = content;
}


public abstract T Visit<T>(ILogVisitor<T> visitor);


}


public class PlaceOrderLog :OrderLog{
public PlaceOrderLog(string content,string orderedBy):base(content)
{
OrderedBy = orderedBy;
}
public string OrderedBy {get;set;}


public override T Visit<T>(ILogVisitor<T> visitor){
return visitor.Visit(this);
}


}


public class MakePaymentLog :OrderLog{
public MakePaymentLog(string content, string paymentGateway, string payedBy):base(content)
{
PaymentGateway = paymentGateway;
PayedBy = payedBy;
}
public string PaymentGateway {get;set;}
public string PayedBy {get;set;}


public override T Visit<T>(ILogVisitor<T> visitor){
return visitor.Visit(this);
}
}


public class OrderCompleteLog : OrderLog{
public OrderCompleteLog(string content, DateTime completeDate):base(content)
{
OrderCompleteDate = completeDate;
}
public DateTime OrderCompleteDate {get;set;}


public override T Visit<T>(ILogVisitor<T> visitor){
return visitor.Visit(this);
}


}


public class OrderLogger{
public static OrderLogger Do
{
get
{
return new OrderLogger();
}
}


public IEnumerable<OrderLog> GetLogs(){
return new List<OrderLog>{
new PlaceOrderLog("place order log","ordered by"),
new MakePaymentLog("make payment log","paypal", "payedBy"),
new OrderCompleteLog("order complete log",DateTime.Now)
};


}




}


now team B want a API , return different log color based on different type
team A give team B a visitor interface , ask them to implement




public interface ILogVisitor<T>{
T Visit(PlaceOrderLog log);
T Visit(MakePaymentLog log);
T Visit(OrderCompleteLog log);
}


team B come out with the implementations


public enum OrderLogColor{Red,Green}


public class OrderLogColorVisitor:ILogVisitor<OrderLogColor>{
public OrderLogColor Visit(PlaceOrderLog log){
return OrderLogColor.Red;
}


public OrderLogColor Visit(MakePaymentLog log){
return OrderLogColor.Green;
}


public OrderLogColor Visit(OrderCompleteLog log){
return OrderLogColor.Green;
}


}


in future 
if team B want more , just implement the ILogVisitor interface 
for example : formatted log


public class OrderLogFormattedVisitor : ILogVisitor <string> {


public string Visit(PlaceOrderLog log){
return "this is place order log formatted information ";
}


public string Visit(MakePaymentLog log){
return "this is make payment log formatted information ";
}


public string Visit(OrderCompleteLog log){
return "this is order complete log formatted information ";
}


}


whats next 
whenever team B want , he just add more visitor to accept different log types returned from teamA


相关文章:

  • 浏览器的几个感悟
  • 本地配置 MongoDb
  • Symbian下获取GSM Cell信息
  • C# 操作MongoDb 错误Element '_v' does not match any field or property of class XXX
  • 双硬盘双系统WINDOWS XP Ubuntu 的启动设置
  • 使用github 配置bitbucket SSH
  • UltraWebGrid动态生成多表头
  • 服务品牌竞争:3G时代的第二战场
  • 使用attribute + 扩展方法完成 enum中field的信息映射
  • 什么是软件项目的成功
  • 在Asp.net MVC 使用bootstrap 的modal dialog 实现Popup
  • VS.NET中解决方案管理器中看不到解决方案节点的解决办法
  • MVC 中使用TreeView
  • .Net的DataSet直接与SQL2005交互
  • MVC4 使用 bootstrap daterangepicker
  • ES2017异步函数现已正式可用
  • ES6 ...操作符
  • JAVA 学习IO流
  • JavaScript 一些 DOM 的知识点
  • JavaScript标准库系列——Math对象和Date对象(二)
  • JavaScript学习总结——原型
  • java第三方包学习之lombok
  • Linux gpio口使用方法
  • MaxCompute访问TableStore(OTS) 数据
  • Python学习笔记 字符串拼接
  • SpiderData 2019年2月23日 DApp数据排行榜
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • vue:响应原理
  • - 概述 - 《设计模式(极简c++版)》
  • 高程读书笔记 第六章 面向对象程序设计
  • 聚类分析——Kmeans
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 2017年360最后一道编程题
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #Linux(make工具和makefile文件以及makefile语法)
  • #Linux(权限管理)
  • #考研#计算机文化知识1(局域网及网络互联)
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (day6) 319. 灯泡开关
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (笔试题)合法字符串
  • (二)pulsar安装在独立的docker中,python测试
  • (二)PySpark3:SparkSQL编程
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (力扣)循环队列的实现与详解(C语言)
  • (十三)Flask之特殊装饰器详解
  • (四)c52学习之旅-流水LED灯
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)h264中avc和flv数据的解析