设计模式

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

2021-01-01  本文已影响0人  凉快先生

在前面组合模式和迭代器模式中,讲了如何把公司组织架构树搭建起来,并且遍历它。现在有这样一个需求,要把公司的所有人员信息打印汇报。

这个类图还是比较简单的,使用了一个模版方法模式,把所要的信息都打印出来,我们先来看一下抽象类:

public abstract class Employee {

    public finial static int MAL = 0;    //男性

    public finial static int FEMAL = 1;    //女性

    // 姓名

    private String name;

    // 薪水

    private int salary;

    // 性别

    private int sex;

    get、set...

    // 打印员工信息

    public final void report(){

         String info = "姓名:" + this.name + "\t";

         info = info + "性别:" + (this.sex == FEMALE?"女":"男") + "\t";

         info = info + "薪水:" + this.salary + "\t"; 

         //获得员工的其他信息

         info = info + this.getOtherInfo();

         System.out.println(info);

    }

    //拼装员工的其他信息

     protected abstract String getOtherInfo();

}

普通员工:

public class CommonEmployee extends Employee {

    //工作内容,这个非常重要,以后的职业规划就是靠这个了

    private String job;

    public String getJob() {

        return job;

    }

    public void setJob(String job) {

        this.job = job;

    }

    protected String getOtherInfo(){

        return "工作:"+ this.job + "\t";

    }

}

经理级人物:

public class Manager extends Employee {

    //这类人物的职责非常明确:业绩

    private String performance;

    public String getPerformance() {

        return performance;

    }

    public void setPerformance(String performance) {

        this.performance = performance;

    }

    protected String getOtherInfo(){

        return "业绩:"+ this.performance + "\t";

    }

}

invoker 类:

public class Client {

    public static void main(String[] args) {

        for(Employee emp:mockEmployee()){

            emp.report();

        }

    }

    //模拟出公司的人员情况,我们可以想象这个数据室通过持久层传递过来的

    public static List<Employee> mockEmployee(){

            List<Employee> empList = new ArrayList<Employee>();

            //产生张三这个员工

            CommonEmployee zhangSan = new CommonEmployee();

            zhangSan.setJob("编写Java程序,绝对的蓝领、苦工加搬运工");

            zhangSan.setName("张三");

            zhangSan.setSalary(1800);

            zhangSan.setSex(Employee.MALE);

            empList.add(zhangSan);

            //产生李四这个员工

            CommonEmployee liSi = new CommonEmployee();

            liSi.setJob("页面美工,审美素质太不流行了!");

            liSi.setName("李四");

            liSi.setSalary(1900);

            liSi.setSex(Employee.FEMALE);

            empList.add(liSi);

            //再产生一个经理

            Manager wangWu = new Manager();

            wangWu.setName("王五");

            wangWu.setPerformance("基本上是负值,但是我会拍马屁呀");

            wangWu.setSalary(18750);

            wangWu.setSex(Employee.MALE);

            empList.add(wangWu);

            return empList;

        }

    }

运行结果:

姓名:张三 性别:男 薪水:1800 工作:编写Java程序,绝对的蓝领、苦工加搬运工

姓名:李四 性别:女 薪水:1900 工作:页面美工,审美素质太不流行了!

姓名:王五 性别:男 薪水:18750 业绩:基本上是负值,但是我会拍马屁呀

结果出来了,也非常正确。我们来想一想这个实际的情况,人力资源部门拿这这份表格会给谁看呢?那当然是大老板了,大老板关心的是什么?关心部门经理的业绩!小兵的情况不是他要了解的。

�大老板就看部门经理的报表,小兵的报表可看可不看;

� 多个大老板,“嗜好”是不同的,主管销售的,则主要关心的营销情况;主管会计的,则主要关心企业的整体财务运行状态;主管技术的,则主要看技术的研发情况;

综合成一句话,这个报表会有修改:数据的修改以及报表的展现修改,按照开闭原则,项目分析的时候已经考虑到这些可能引起变更的隐私,就需要在设计时考虑通过扩展来来避开未来需求变更而引起的代码修改风险。我们来想一想,每个普通员工类和经理类都一个方法 report,那是否可以把这个方法提取到另外一个类中来实现呢?

原有的示意图如下:

这两个类都一个相同的方法 report(),但是要实现的内容不相同,而且还有可能会发生变动,那我们就让其他类来实现这个 report 方法,好,看示意图图的变更:

两个类的 report 方法都不需要了,只有 Visitor 类来实现了 report 的方法

在抽象类 Employee 中增加了 accept 方法,这个方法是定义我这个类可以允许被谁来访问,也就定义一类访问者,在具体的实现类中调用访问者的方法。我们先看访问者接口 IVisitor 程序:

public interface IVisitor { 

    //首先定义我可以访问普通员工

    public void visit(CommonEmployee commonEmployee);

    //其次定义,我还可以访问部门经理

    public void visit(Manager manager);

}

访问者的实现类: 

public class Visitor implements IVisitor {

    //访问普通员工,打印出报表

    public void visit(CommonEmployee commonEmployee) {

        System.out.println(this.getCommonEmployee(commonEmployee));

    }

    //访问部门经理,打印出报表

    public void visit(Manager manager) {

        System.out.println(this.getManagerInfo(manager));

    }

    //组装出基本信息

    private String getBasicInfo(Employee employee){

        String info = "姓名:" + employee.getName() + "\t";

        info = info + "性别:" + (employee.getSex() == Employee.FEMALE?"女":"男") + "\t";

        info = info + "薪水:" + employee.getSalary() + "\t";

        return info;

    }

    //组装出部门经理的信息

    private String getManagerInfo(Manager manager){

        String basicInfo = this.getBasicInfo(manager);

        String otherInfo = "业绩:"+manager.getPerformance() + "\t";

        return basicInfo + otherInfo;

    }

    //组装出普通员工信息

    private String getCommonEmployee(CommonEmployee commonEmployee){

        String basicInfo = this.getBasicInfo(commonEmployee);

        String otherInfo = "工作:"+commonEmployee.getJob()+"\t";

        return basicInfo + otherInfo;

    }

}

在具体的实现类中,定义了两个私有方法,作用就是产生需要打印的数据和格式,然后在访问者访问相关的对象时,产生这个报表。继续看 Employee 抽象类:

public abstract class Employee {

    public final static int MALE = 0; //0代表是男性

    public final static int FEMALE = 1; //1代表是女性

    //甭管是谁,都有工资

    private String name;

    //只要是员工那就有薪水

    private int salary;

    //性别很重要

    private int sex;

    get、set...

    //我允许一个访问者过来访问

    public abstract void accept(IVisitor visitor);

}

删除了 report 方法,增加了 accept 方法,需要实现类来实现。继续看实现类:

普通员工:

public class CommonEmployee extends Employee {

    //工作内容,这个非常重要,以后的职业规划就是靠这个了

    private String job;

    public String getJob() {

        return job;

    }

    public void setJob(String job) {

        this.job = job;

    }

    //我允许访问者过来访问

    @Override

    public void accept(IVisitor visitor){

        visitor.visit(this);

    }

}

经理级人物:

public class Manager extends Employee {

    //这类人物的职责非常明确:业绩

    private String performance;

    public String getPerformance() {

        return performance;

    }

    public void setPerformance(String performance) {

        this.performance = performance;

    }

    //部门经理允许访问者访问

    @Override

    public void accept(IVisitor visitor){

        visitor.visit(this);

    }

}

public class Client {

    public static void main(String[] args) {

        for(Employee emp:mockEmployee()){

            emp.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(1800);

        zhangSan.setSex(Employee.MALE);

        empList.add(zhangSan);

        //产生李四这个员工

        CommonEmployee liSi = new CommonEmployee();

        liSi.setJob("页面美工,审美素质太不流行了!");

        liSi.setName("李四");

        liSi.setSalary(1900);

        liSi.setSex(Employee.FEMALE);

        empList.add(liSi);

        //再产生一个经理

        Manager wangWu = new Manager();

        wangWu.setName("王五");

        wangWu.setPerformance("基本上是负值,但是我会拍马屁呀");

        wangWu.setSalary(18750);

        wangWu.setSex(Employee.MALE);

        empList.add(wangWu);

        return empList;

    }

}

看运行结果:

姓名:张三 性别:男 薪水:1800 工作:编写Java程序,绝对的蓝领、苦工加搬运工

姓名:李四 性别:女 薪水:1900 工作:页面美工,审美素质太不流行了!

姓名:王五 性别:男 薪水:18750 业绩:基本上是负值,但是我会拍马屁呀

� 首先通过循环遍历所有元素;

� 其次,每个员工对象都定义了一个访问者;

� 再其次,员工对象把自己做为一个参数调用访问者 visit 方法;

� 然后,访问者调用自己内部的计算逻辑,计算出相应的数据和表格元素;

� 最后,访问者打印出报表和数据;

访问者模式通用类图:

上一篇下一篇

猜你喜欢

热点阅读