真刀实枪之访问者模式

  • 从员工的隐私说起

    1. 通过组合模式可以把整个员工的人员信息都整合起来形成一张企业员工信息树
    2. 通过迭代器模式可以遍历公司所有的员工
  • 现在要把公司的所有人员的信息都汇总起来,那该怎么办,作为一名逻辑工作者,当然先开始设计啦

    • 类图

    • 类图有了,先按照类图来实现一下吧!

      • Employee

        package com.peng.fwz;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public abstract class Employee {// 0代表男性public final static int MALE = 0;// 1代表女性public final static int FEMALE = 1;// 名字private String name;// 薪水private int salary;// 性别private int sex;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSalary() {return salary;}public void setSalary(int salary) {this.salary = salary;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}// 打印出员工的信息public final void report() {String info = "名字:" + this.name + ",薪水:" + this.salary + ",性别:"+ this.sex;// 获得员工的其他信息info += this.getOtherInfo();System.out.println(info);}// 拼装员工的其他信息protected abstract String getOtherInfo();
        }
        
      • CommonEmployee

        package com.peng.fwz;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class CommonEmployee extends Employee {// 工作private String job;public String getJob() {return job;}public void setJob(String job) {this.job = job;}@Overrideprotected String getOtherInfo() {return ",工作:"+this.job;}}
        
      • Manager

        package com.peng.fwz;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class Manager extends Employee {// 职责private String performance;public String getPerformance() {return performance;}public void setPerformance(String performance) {this.performance = performance;}@Overrideprotected String getOtherInfo() {return ",业绩:" + this.performance;}}
        
      • Client

        package com.peng.fwz;import java.util.ArrayList;
        import java.util.List;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class Client {public static void main(String[] args) {for (Employee e : mockEmployee()) {e.report();}}// 模拟公司员工情况public static List<Employee> mockEmployee() {List<Employee> empList = new ArrayList<Employee>();// 生产张三这个员工CommonEmployee zhangSan = new CommonEmployee();zhangSan.setJob("编写java程序~~");zhangSan.setName("张三");zhangSan.setSalary(66666);zhangSan.setSex(Employee.MALE);empList.add(zhangSan);// 生产李四这个员工CommonEmployee liSi = new CommonEmployee();liSi.setJob("页面美工~~");liSi.setName("李四");liSi.setSalary(22222);liSi.setSex(Employee.FEMALE);empList.add(liSi);// 生产一个经理Manager wangWu = new Manager();wangWu.setPerformance("管理项目~~");wangWu.setName("王五");wangWu.setSalary(88888);wangWu.setSex(Employee.MALE);empList.add(wangWu);// 返回该列表return empList;}
        }
        
      • 执行结果

        名字:张三,薪水:66666,性别:0,工作:编写java程序~~
        名字:李四,薪水:22222,性别:1,工作:页面美工~~
        名字:王五,薪水:88888,性别:0,业绩:管理项目~~
        
  • 结果出来了,不过有点问题
    1. 各个部门的级别不同,管理的人眼也就不同,没有必要都把所有的人员的信息都看一遍
    2. 这个报表可能会改,展现形式也有可能改变--按照开闭原则,report方法已经写死,不太符合,子类不能自行修改
  • 遇到问题就改呗,先从设计图纸下手
    • 类图

  • 类图改好了,如何能为每个子类订制报表的方法呢?report方法是否可以提取到另外一个类Visitor中来实现,继续修改类图
    • 类图

  • 两个子类的report方法都不需要了,感觉把方法委托出去了一样,但这与委托相去甚远,详细类图继续改造
    • 类图

  • 在抽象类Employee类中增加了一个accept方法,该方法是一个抽象方法,由子类实现,其意义就是说我这类可以允许谁可以访问。在具体的实现类中调用访问者的方法

    • 代码

      • IVisitor

        package fwz2;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public interface IVisitor {// 可以访问普通员工public void visit(CommonEmployee commonEmployee);// 可以访问管理员public void visit(Manager manager);
        }
        
      • Visitor

        package fwz2;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class Visitor implements IVisitor {// 访问普通员工--打印出报表@Overridepublic void visit(CommonEmployee commonEmployee) {System.out.println(this.getCommonEmployee(commonEmployee));}// 访问管理者,打印出报表@Overridepublic void visit(Manager manager) {System.out.println(this.getManager(manager));}// 组装出基本信息private String getBasicInfo(Employee employee) {String info = "名字:" + employee.getName() + ",薪水:"+ employee.getSalary() + ",性别:"+ (employee.getSex() == Employee.FEMALE ? "女" : "男");return info;}// 组装出部门经理信息private String getManager(Manager manager) {String basicInfo = this.getBasicInfo(manager);String otherInfo = "业绩:" + manager.getPerformance();return basicInfo + otherInfo;}// 组装出普通员工信息private String getCommonEmployee(CommonEmployee commomEmployee) {String basicInfo = this.getBasicInfo(commomEmployee);String otherInfo = "业绩:" + commomEmployee.getJob();return basicInfo + otherInfo;}}
        
      • Employee

        package fwz2;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public abstract class Employee {// 0代表男性public final static int MALE = 0;// 1代表女性public final static int FEMALE = 1;// 名字private String name;// 薪水private int salary;// 性别private int sex;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSalary() {return salary;}public void setSalary(int salary) {this.salary = salary;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}// 允许访问者访问public abstract void accept(IVisitor visitor);
        }
        
      • CommonEmployee

        package fwz2;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class CommonEmployee extends Employee {// 工作private String job;public String getJob() {return job;}public void setJob(String job) {this.job = job;}@Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}}
        
      • Manager

        package fwz2;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class Manager extends Employee {// 职责private String performance;public String getPerformance() {return performance;}public void setPerformance(String performance) {this.performance = performance;}@Overridepublic void accept(IVisitor visitor) {visitor.visit(this);}}
        
      • Client

        package fwz2;import java.util.ArrayList;
        import java.util.List;/*** @author kungfu~peng* @data 2017年11月27日* @description*/
        public class Client {public static void main(String[] args) {for (Employee e : mockEmployee()) {e.accept(new Visitor());}}// 模拟公司员工情况public static List<Employee> mockEmployee() {List<Employee> empList = new ArrayList<Employee>();// 生产张三这个员工CommonEmployee zhangSan = new CommonEmployee();zhangSan.setJob("编写java程序~~");zhangSan.setName("张三");zhangSan.setSalary(66666);zhangSan.setSex(Employee.MALE);empList.add(zhangSan);// 生产李四这个员工CommonEmployee liSi = new CommonEmployee();liSi.setJob("页面美工~~");liSi.setName("李四");liSi.setSalary(22222);liSi.setSex(Employee.FEMALE);empList.add(liSi);// 生产一个经理Manager wangWu = new Manager();wangWu.setPerformance("管理项目~~");wangWu.setName("王五");wangWu.setSalary(88888);wangWu.setSex(Employee.MALE);empList.add(wangWu);// 返回该列表return empList;}
        }
        
      • 执行结果

        名字:张三,薪水:66666,性别:男业绩:编写java程序~~
        名字:李四,薪水:22222,性别:女业绩:页面美工~~
        名字:王五,薪水:88888,性别:男业绩:管理项目~~
        
  • 首先看看IVisitor这个类,它是可以控制报表格式的,而其他类都不用修改【Spring中的接口注入】

访问者模式的定义

  • Visitor Pattern
  • Respresent 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 element on witch it operates.(封装一些作用于某种数据结构中的各元素的操作,它可以不在改变数据结构的前提下定义作用于这些元素的新的操作)

访问者模式的通用类图

  • 解释
    1. Visitor抽象访问者
    2. ConcreteVisitor具体访问者
    3. Element抽象元素
    4. ConcreteElement具体元素
    5. ObjectElement结构对象--【List、Set、Map】

访问者模式的通用代码--类图有了,例子也有了,还不快自己动手试试~~

  • 注意事项:ObjectElement:用容器来装数据即可,别想那么复杂

访问者模式的应用

  • 访问者模式的优点

    1. 符合单一的原则
    2. 优秀的扩展性
    3. 灵活性非常高
  • 访问者模式的缺点
    1. 具体元素对访问者公布细节
    2. 具体元素变更比较困难
    3. 违背了依赖倒置的原则【访问者依赖的是具体元素,而不是抽象元素,这就破坏了依赖倒置原则】
  • 访问者模式的使用场景
    1. 一个对象结构包含很对类对象,他们都由不同的接口,而你相对这些对象实施一些依赖于具体类的操作,也就是说迭代器已经不能胜任的事情
    2. 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而避免让这些操作“污染”这些对象的类【访问者模式是对迭代器模式的扩充,可以遍历不同的对象,针对的对象不同,然后执行的操作也不同】
    3. 访问者模式还有一个用途:充当拦截器(Interceptor)角色--这将在混编模式中讲解

访问者模式的扩展

  • 统计功能:数据统计+报表+批处理

    • 类图简单演示

  • 多个访问者:展示表+汇总表
    • 类图简单演示

双分派[Double Dispatch]

  • 单分派【Single Dispatch】:单分派语言处理一个操作是根据请求者的名称和接收到的参数决定的,在java中,有静态和动态绑定之说,它的实现是依据重载和覆写实现的
  • 多分派【Mutilate Dispatch】
  • 双分派:无论角色怎么变,都能够用同一个方法来执行【多分派的特例】
    //核心:this这个类是Method方法的参数
    public void accept(IObject obj){obj.method(this);
    }
    

最佳实践

  • 访问者模式是一种集中规整模式,特别适合大规模重构项目--功能集中化
  • 还可以与其他模式混编建立一套自己的过滤器或拦截器

声明

  • 摘自秦小波《设计模式之禅》第2版;
  • 仅供学习,严禁商业用途;
  • 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正;

设计模式之禅【访问者模式】相关推荐

  1. 设计模式之禅-访问者模式

    目录 访问者模式 例子 定义 优点 缺点 使用场景 扩展 统计功能 多个访问者 双分派 最佳实践 访问者模式 例子 员工信息,获取不同类型员工的信息. public interface IVisito ...

  2. 设计模式学习之访问者模式

    访问者模式,是行为型设计模式之一.访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是 ...

  3. 设计模式--23、访问者模式

    访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作.例如处方单中的各种药品信息就是被访问 ...

  4. 设计模式学习之访问者模式(Visitor,行为型模式)(21)

    参考:https://www.cnblogs.com/edisonchou/p/7247990.html 在患者就医时,医生会根据病情开具处方单,很多医院都会存在以下这个流程:划价人员拿到处方单之后根 ...

  5. C#设计模式系列:访问者模式(Visitor)

    1.访问者模式简介 1.1>.定义 作用于某个对象群中各个对象的操作,可以使在不改变对象本身的情况下,定义作用于对象的新操作. 1.2>.使用频率   低 2.访问者模式结构 2.1> ...

  6. 《研磨设计模式》chap25 访问者模式Visitor(3)联合组合模式+总结

    1. 联合组合模式 //抽象的组件对象,相当于访问者模式中的元素对象 public abstract class Component {//接受访问者的访问 public abstract void ...

  7. 《研磨设计模式》chap25 访问者模式Visitor(2)模式应用到场景

    1. 实现 //各种客户的父类 public abstract class Customer { private String customerId;// 客户编号 private String na ...

  8. 设计模式笔记(24)---访问者模式(行为型)

    Gof定义 表示一个作用于某对象结构中的各元素的操作.它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作. 动机 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法 ...

  9. Java描述设计模式(23):访问者模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 电竞是游戏比赛达到"竞技"层面的体育项目.利用电子设备作为运动器械进行的.人与人之间的智力对抗 ...

  10. Java 设计模式之 Visitor 访问者模式

    Visitor 访问者模式适用的场景是,存在某个容器,里面的组成部分不会发生变化,对这些组成部分的访问,不是由这个容器决定,而是交给访问者决定. 举个生活中的例子,比如稻草人,卖包,卖鞋子这些固定的消 ...

最新文章

  1. 剑指offer java版(三)
  2. ftp 追加远程文件_ftp上传,完成ftp定时上传、下载只需3步
  3. 应届生想要获取web前端开发岗位?这份技能攻略,面试攻略别错过!
  4. 如何在Xcode 4中设置NSZombieEnabled?
  5. Training a classifier
  6. C++11新特性之lambda表达式
  7. mysql外键设置sql语句,SQL Server 2008之SQL语句外键
  8. java 微服务实践 视频,全新JAVA微服为务实战Spring Boot系列视频教程 小马哥 JAVA微服务实践视频课程...
  9. 系统地址和服务器地址不一致,在打开远程通服务配置程序时检测系统配置时提示配置失败,映射的ip地址与服务器不一致。...
  10. 课程 2B: 制作一款交互性应用
  11. ci mysql 缓存_CI框架缓存的实现原理
  12. 巨量引擎初级营销认证题库_“移动营销,智赢未来”巨量引擎4月招商加盟专场沙龙圆满落幕...
  13. 浅谈Redis基本数据类型底层编码(含C源码)
  14. js读取服务器上的文件,js读取远程文件服务器
  15. 狮子鱼社区团购小程序独立版 安装教程及后台设置小程序APPID,微信支付,腾讯地图AppKey
  16. LCP 19. 秋叶收藏集(C++)---动态规划解题
  17. sci国外期刊投稿过程(已完结)
  18. 三维重建方法--激光or视觉
  19. 我的世界服务器修改速度,我的世界速度提升指令是什么_我的世界速度提升指令介绍_玩游戏网...
  20. Unity HybridCLR热更新技术实现

热门文章

  1. chrome浏览器的性能测试--本文引用自 百度百科http://baike.baidu.com/view/1835504.htm
  2. 独立站运营 | 聊天聚合部件,助你提升100%私域转化率
  3. Java实现 LeetCode 765 情侣牵手(并查集 || 暴力)
  4. SmtpJS发送邮件教程
  5. html图片高度撑开,CSS背景图撑开盒子高度
  6. 360cdn能挡住cc攻击_揭秘为何使用CDN后一但被cc攻击网站就瘫痪的真正原因?
  7. 使用Telnet连接smtp服务器发送邮件
  8. 数据中心服务器芯片温度,【DKV】新建数据中心气流——第二部分:入口温度VS服务器性能...
  9. 为什么影子会相互吸引? - 《像乌鸦一样思考》
  10. Android应用在新浪微博授权提示:文件不存在 C8998 的解决方法