访问者模式
传统方式的问题分析
1.如果系统比较小,还是OK的,但是考虑到系统增加越来越多的新的功能时,对代码修改是比较大的。违反了OCP原则。
2.扩展性不好,比如增加了新的人员类型,或者管理方法,都不好做。
作用域这些元素的新的操作。
2.主要将数据结构与数据操作分离,解决数据结构和操作耦合性的问题。
3.访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
4.访问者模式主要应用场景是:需要对一个对象结构找那个的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作“污染”这些对象的类,可以选用访问者模式解决。
当然,访问者模式也可以这样进行理解,比方说一张药品处方单(上面有要开的药品,还有这些药品加起来的价格),让划价员看到的时候,他只关心这些药品的总金额,不关心你上面具体是什么药品,让药房处理人员看到这张药品处方单子,他就只关心上面是什么药品,因为他要给你开具体的药。这也就是不同的访问者对同一个对象作出的不同的评价。
代码实现
1 2 3 4 5 6 7 8 | package com.visitor; public abstract class Action { //得到男性的测评 public abstract void getManResult(Man man); //得到女性的测评 public abstract void getWomanResult(Woman woman); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.visitor; public class Success extends Action { @Override public void getManResult(Man man) { System.out.println( "男人给的评价是很成功" ); } @Override public void getWomanResult(Woman woman) { System.out.println( "女人给的评价是很成功" ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.visitor; public class Fail extends Action { @Override public void getManResult(Man man) { System.out.println( "男人给的评价是很失败" ); } @Override public void getWomanResult(Woman woman) { System.out.println( "女人给的评价是很失败" ); } } |
1 2 3 4 5 6 | package com.visitor; public abstract class Person { //提供一个方法,让访问者可以访问 public abstract void accept(Action action); } |
1 2 3 4 5 6 7 8 | package com.visitor; public class Man extends Person { @Override public void accept(Action action) { action.getManResult( this ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.visitor; import java.util.TreeSet; /** * 说明: * 1.这里我们使用到了双分派,即首先在客户端程序中,将具体状态作为参数传递到Woman中(第一次分派) * 2.然后 Woman 类调用作为参数的“具体方法”中方法getWomanResult,同时将自己this作为参数传入,完成第二次分派。 */ public class Woman extends Person { @Override public void accept(Action action) { action.getWomanResult( this ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.visitor; import java.util.LinkedList; import java.util.List; /** * 数据结构,管理了很多人(有man和woman) */ public class ObjectStructrue { //维护了一个集合 private List<Person> persons= new LinkedList<>(); //增加到LinkedList中 public void attach(Person person){ persons.add(person); } //移除 public void detach(Person person){ persons.remove(person); } //显示测评情况 public void display(Action action){ for (Person p: persons) { p.accept(action); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.visitor; import sun.java2d.opengl.OGLContext; import java.io.ObjectOutputStream; public class Client { public static void main(String[] args) { //创建ObjectStructure ObjectStructrue objectStructrue = new ObjectStructrue(); objectStructrue.attach( new Man()); objectStructrue.attach( new Woman()); //成功 Success success = new Success(); objectStructrue.display(success); System.out.println( "======================" ); //失败 Fail fail = new Fail(); objectStructrue.display(fail); } } |
运行结果:
1 2 3 4 5 | 男人给的评价是很成功 女人给的评价是很成功 ====================== 男人给的评价是很失败 女人给的评价是很失败 |
-上面提到了双分派,所谓双分派是指不管类怎样变化,我们都能找到期望的方法运行,双分派意味着得到执行的操作取决于请求的种类和两个接受者的类型。
-以上述实例为例,假设我们需要添加一个undetermined的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需要增加一个Action子类即可在客户端调用即可,不需要改动任何代码。
访问者模式的注意事项和细节
优点:
1.访问者模式符合单一职责原则、让陈旭具有优秀的扩展性、灵活性非常高
2.访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
1.具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
2.违背了依赖倒转原则,访问者依赖的是具体元素,而不是抽象元素
3.因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY