访问者
访问者模式有点小复杂,而且需要在实际的时候提前留好后门,下面通过简单的例子来介绍一下。场景就是邮件发送,通知客户怎么了怎么了,后面可能需要短信或者app也推。推之前或推送之后干点别的。
public abstract class BaseJob { public abstract void Execute();public abstract void Accept(Monitoring monitoring); }
这里的Execute方法就是正常操作,Accept就是预留的方法,通过它可以访问BaseJob 的实现,并且在不改动BaseJob 的情况下。那么Monitoring这个对象是什么呢?
public abstract class Monitoring { public abstract void Monitor(SendEmail sendEmail); public abstract void Monitor(SendMessage sendMessage); public abstract void Monitor(SendOther endOther); }
所谓访问者Monitoring肯定是 BaseJob 的访问者,所以SendEmail 、SendMessage 、SendOther 都是BaseJob 的实现
public class SendEmail : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("邮件发送"); } } public class SendMessage : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("短信发送"); } } public class SendOther : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("App发送"); } }
被访问的对象实现了,这个访问者也先给实现了吧。
public class AutoMonitor: Monitoring { public override void Monitor(SendEmail sendEmail) { Debug.WriteLine("邮件发送前"); sendEmail.Execute(); Debug.WriteLine("邮件发送后"); } public override void Monitor(SendMessage sendMessage) { Debug.WriteLine("短信发送前"); sendMessage.Execute(); Debug.WriteLine("短信发送后"); } public override void Monitor(SendOther endOther) { Debug.WriteLine("app发送前"); endOther.Execute(); Debug.WriteLine("app发送后"); } }
下面就是怎么去调用的问题了。通过一个director类去包装一下访问者和被访问者。
public class Director { private Monitoring _monitoring; public Director(Monitoring monitoring) { _monitoring = monitoring; } public void Start(BaseJob baseJob) { baseJob.Accept(_monitoring); } }
director的代码有点反转再反转的味道,Start方法是我们访问的入口,通过传一个被访问者对象,调用被访问者的Accept的方法,被访问者Accept又需要一个访问者的对象,这里就通过构造函数传进来的,目前为止都是抽象的,还未具体指定实现。
执行的流程就是start=>accept=》moitor,director入口,到basejob的accept拿到访问者monitor,mointor的实现moitor又可以拿到完整的被访问者basejob,这样就可以操纵被访问者了,而且可以在访问者前后添加任何扩展自己的操作。
有点绕,但是这个也实现了aop,跟装饰器效果也差不多了。
下面就是客户端的调用了,这里需要访问SendEmail 、SendMessage 、SendOther 都可以灵活的替换掉。
public void TestV() { Monitoring monitoring = new AutoMonitor(); Director director = new Director(monitoring); director.Start(new SendEmail()); }
写到这里差不多了,留个彩蛋自己体会:
public class Director { private Monitoring _monitoring; public Director(Monitoring monitoring) { _monitoring = monitoring; } public void Start(BaseJob baseJob) { baseJob.Accept(_monitoring); } } public class AutoMonitor: Monitoring { public override void Monitor(BaseJob baseJob) { Debug.WriteLine("发送前动作"); baseJob.Execute(); Debug.WriteLine("发送后动作"); } } public class SendEmail : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("邮件发送"); } } public class SendMessage : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("短信发送"); } } public class SendOther : BaseJob { public override void Accept(Monitoring monitoring) { monitoring.Monitor(this); } public override void Execute() { Debug.WriteLine("App发送"); } } public abstract class BaseJob { public abstract void Execute();public abstract void Accept(Monitoring monitoring); } public abstract class Monitoring { public abstract void Monitor(BaseJob baseJob); }
Monitoring monitoring = new AutoMonitor(); Director director = new Director(monitoring); director.Start(new SendEmail()); director.Start(new SendOther());
补上计算器的例子:
public class CalClient { protected IColleague _colleague; public CalClient(IColleague colleague) { _colleague = colleague; } public decimal Start(ICalculator calculator) { return calculator.Accept(_colleague); } } public class ColleaguaA : IColleague { protected List<decimal> _items; public ColleaguaA(List<decimal> items) { _items = items; } public decimal Invoke(ICalculator calculator) { return calculator.Calculate(_items.ToArray()); } } public class ColleaguaB : IColleague { private List<decimal> items; public List<decimal> Items { get => items; set => items = value; } public decimal Invoke(ICalculator calculator) { Trace.WriteLine("items赋值判断"); var result = calculator.Calculate(Items.ToArray()); Trace.WriteLine(" save to db"); return result; } } public interface ICalculator { decimal Calculate(params decimal[] items); decimal Accept(IColleague colleague); } public interface IColleague { decimal Invoke(ICalculator calculator); } public class AddCalculator : ICalculator { public decimal Accept(IColleague colleague) { return colleague.Invoke(this); } public decimal Calculate(params decimal[] items) { return items.Aggregate((a, b) => a + b); } } public class SubCalculator : ICalculator { public decimal Accept(IColleague colleague) { return colleague.Invoke(this); } public decimal Calculate(params decimal[] items) { return items.Aggregate((a, b) => a - b); } } public class MulCalculator : ICalculator { public decimal Accept(IColleague colleague) { return colleague.Invoke(this); } public decimal Calculate(params decimal[] items) { return items.Aggregate((a, b) => a * b); } } public class DivCalculator : ICalculator { public decimal Accept(IColleague colleague) { return colleague.Invoke(this); } public decimal Calculate(params decimal[] items) { return items.Aggregate((a, b) => a / b); } }
ColleaguaA colleaguaA = new ColleaguaA(new List<decimal> { 12, 34, 56 }); ColleaguaB colleaguaB = new ColleaguaB(); colleaguaB.Items = new List<decimal> { 1, 2, 3, 4, 5 }; CalClient calClient = new CalClient(colleaguaA); calClient.Start(new AddCalculator()); CalClient otherClient = new CalClient(colleaguaB); otherClient.Start(new MulCalculator());
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)