018.访问者模式

2021-01-05  本文已影响0人  CoderJed

现在有这样一个需求,我要把公司中的所有人员信息都打印汇报上去,每一个员工都有这些信息:名字、性别、薪水,我们来看类图:

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

/**
 * @description 在一个单位里谁都是员工,甭管你是部门经理还是小兵
 */
public abstract class Employee {

    // 0代表男性
    public static final int MALE = 0;
    // 1代表女性
    public static final int FEMALE = 1;

    private String name;
    private int salary;
    private int sex;

    public final void report() {
        System.out.println("姓名: " + name + "\t性别: " + (sex == FEMALE ? "女" : "男") + "\t薪水: " + salary + "\t" + getOtherInfo());
    }

    protected abstract String getOtherInfo();

    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;
    }
}

再看小兵的实现类:

/**
 * @description 普通员工,也就是最小的小兵
 */
public class CommonEmployee extends Employee {

    // 工作内容
    private String job;

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    protected String getOtherInfo() {
        return "工作: " +job + "\t";
    }
}

再来看领导阶层:

/**
 * @description 经理级人物
 */
public class Manager extends Employee {

    // 业绩
    private String performance;

    public String getPerformance() {
        return performance;
    }

    public void setPerformance(String performance) {
        this.performance = performance;
    }

    @Override
    protected String getOtherInfo() {
        return "业绩: " + performance + "\t";
    }
}

然后我们来看一下我们的invoker类:

public class Client {

    public static void main(String[] args) {

        for (Employee employee : mockEmployee()) {
            employee.report();
        }

    }

    public static List<Employee> mockEmployee() {
        List<Employee> empList = new ArrayList<>();

        CommonEmployee zhangSan = new CommonEmployee();
        zhangSan.setJob("Java开发");
        zhangSan.setName("张三");
        zhangSan.setSalary(1800);
        zhangSan.setSex(Employee.MALE);

        CommonEmployee liSi = new CommonEmployee();
        liSi.setJob("前端开发");
        liSi.setName("李四");
        liSi.setSalary(1900);
        liSi.setSex(Employee.FEMALE);

        Manager wangWu = new Manager();
        wangWu.setPerformance("基本都是负值");
        wangWu.setName("王五");
        wangWu.setSalary(18750);
        wangWu.setSex(Employee.FEMALE);

        empList.add(zhangSan);
        empList.add(liSi);
        empList.add(wangWu);

        return empList;
    }

}

程序运行结果:

姓名: 张三  性别: 男   薪水: 1800    工作: Java开发  
姓名: 李四  性别: 女   薪水: 1900    工作: 前端开发    
姓名: 王五  性别: 女   薪水: 18750   业绩: 基本都是负值

接下来改进我们的代码:

每个普通员工类和经理类都一个report()方法,它们要实现的内容不相同,而且还有可能会发生变动,那我们就让其他类来实现这个 report()方法:

看代码实现:

public interface IVisitor {

    // 定义可以访问普通员工
    void visit(CommonEmployee commonEmployee);

    // 定义可以访问部门经理
    void visit(Manager manager);

}

public class Visitor implements IVisitor {

    // 组装基本信息
    private String getBasicInfo(Employee employee) {
        return "姓名: " + employee.getName() + "\t性别: " +
                (employee.getSex() == Employee.FEMALE ? "女" : "男") + "\t薪水: " +
                employee.getSalary() + "\t";
    }

    // 组装部门经理的信息
    private String getManagerInfo(Manager manager) {
        return getBasicInfo(manager) + "业绩: " + manager.getPerformance() + "\t";
    }

    // 组装普通员工的信息
    private String getCommonEmployeeInfo(CommonEmployee commonEmployee) {
        return getBasicInfo(commonEmployee) + "工作: " + commonEmployee.getJob() + "\t";
    }

    @Override
    public void visit(CommonEmployee commonEmployee) {
        System.out.println(getCommonEmployeeInfo(commonEmployee));
    }

    @Override
    public void visit(Manager manager) {
        System.out.println(getManagerInfo(manager));
    }

}

public abstract class Employee {

    // 0代表男性
    public static final int MALE = 0;
    // 1代表女性
    public static final int FEMALE = 1;

    private String name;
    private int salary;
    private int sex;

    // 一个访问者过来访问
    public abstract void accept(IVisitor visitor);

    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 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 employee : mockEmployee()) {
            employee.accept(new Visitor());
        }

    }

    public static List<Employee> mockEmployee() {
        List<Employee> empList = new ArrayList<>();

        CommonEmployee zhangSan = new CommonEmployee();
        zhangSan.setJob("Java开发");
        zhangSan.setName("张三");
        zhangSan.setSalary(1800);
        zhangSan.setSex(Employee.MALE);

        CommonEmployee liSi = new CommonEmployee();
        liSi.setJob("前端开发");
        liSi.setName("李四");
        liSi.setSalary(1900);
        liSi.setSex(Employee.FEMALE);

        Manager wangWu = new Manager();
        wangWu.setPerformance("基本都是负值");
        wangWu.setName("王五");
        wangWu.setSalary(18750);
        wangWu.setSex(Employee.FEMALE);

        empList.add(zhangSan);
        empList.add(liSi);
        empList.add(wangWu);

        return empList;
    }

}

运行结果也完全相同,那回过头我们来看看这个程序是怎么实现的:

这样一来,如果打印的信息格式发生变化了,我只要修改Visitor的实现或者再产生一个Visitor就可以产生一个新的报表格式,而其他的类都不用修改。

以上讲的就是访问者模式,这个模式的通用类图如下:

看了这个通用类图,大家可能要犯迷糊了,这里怎么有一个ObjectStruture这个类呢?你刚刚举得例子就没有呢?真没有吗?我们不是定义了一个List了吗?这就是一个ObjectStruture,我们来看这几个角色的职责:

接下来我们来思考一下,访问者可以用在什么地方。在这种地方你一定要考虑到使用访问者模式:业务规则要求遍历多个不同的对象。这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据,(当然了,你使用instanceof的话,能访问所有的数据,这个不争论),而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。访问者模式还有一个用途,就是充当拦截器(Interceptor)角色,这个我们在后边来讲。

访问者模式有哪些优点呢?

访问者模式的缺点也很明显:

访问者模式是有缺点的,是事物都有缺点,但是这仍然掩盖不了它的光芒,访问者模式结合其他模式比如模版方法模式、状态模式、解释器模式、代理模式等就会非常强大,这个我们放在模式混编中来讲解。

在这里我提出三个扩展的功能共大家参考:

看代码实现:

public interface IVisitor {

    // 定义可以访问普通员工
    void visit(CommonEmployee commonEmployee);

    // 定义可以访问部门经理
    void visit(Manager manager);

    // 统计所有员工工资总和
    int getTotalSalary();

}

public class Visitor implements IVisitor {

    // 部门经理的工资系数是5
    private static final int MANAGER_COEFFICIENT = 5;
    // 员工的工资系数是2
    private static final int COMMON_EMPLOYEE_COEFFICIENT = 2;

    // 普通员工的工资总和
    private int commonTotalSalary = 0;
    // 部门经理的工资总和
    private int managerTotalSalary = 0;

    // 组装基本信息
    private String getBasicInfo(Employee employee) {
        return "姓名: " + employee.getName() + "\t性别: " +
                (employee.getSex() == Employee.FEMALE ? "女" : "男") + "\t薪水: " +
                employee.getSalary() + "\t";
    }

    // 组装部门经理的信息
    private String getManagerInfo(Manager manager) {
        return getBasicInfo(manager) + "业绩: " + manager.getPerformance() + "\t";
    }

    // 组装普通员工的信息
    private String getCommonEmployeeInfo(CommonEmployee commonEmployee) {
        return getBasicInfo(commonEmployee) + "工作: " + commonEmployee.getJob() + "\t";
    }

    // 计算部门经理的工资总和
    private void calManagerSalary(int salary) {
        this.managerTotalSalary += salary * MANAGER_COEFFICIENT;
    }

    // 计算普通员工的工资总和
    private void calCommonSalary(int salary) {
        this.commonTotalSalary += salary * COMMON_EMPLOYEE_COEFFICIENT;
    }

    @Override
    public int getTotalSalary() {
        return managerTotalSalary + commonTotalSalary;
    }

    @Override
    public void visit(CommonEmployee commonEmployee) {
        System.out.println(getCommonEmployeeInfo(commonEmployee));
        // 计算普通员工的薪水总和
        calCommonSalary(commonEmployee.getSalary());
    }

    @Override
    public void visit(Manager manager) {
        System.out.println(getManagerInfo(manager));
        // 计算部门经理的薪水总和
        calManagerSalary(manager.getSalary());
    }

}

public class Client {

    public static void main(String[] args) {
        IVisitor visitor = new Visitor();
        for (Employee employee : mockEmployee()) {
            employee.accept(visitor);
        }
        System.out.println("工资总额: " + visitor.getTotalSalary());
    }

    public static List<Employee> mockEmployee() {
        List<Employee> empList = new ArrayList<>();

        CommonEmployee zhangSan = new CommonEmployee();
        zhangSan.setJob("Java开发");
        zhangSan.setName("张三");
        zhangSan.setSalary(1800);
        zhangSan.setSex(Employee.MALE);

        CommonEmployee liSi = new CommonEmployee();
        liSi.setJob("前端开发");
        liSi.setName("李四");
        liSi.setSalary(1900);
        liSi.setSex(Employee.FEMALE);

        Manager wangWu = new Manager();
        wangWu.setPerformance("基本都是负值");
        wangWu.setName("王五");
        wangWu.setSalary(18750);
        wangWu.setSex(Employee.FEMALE);

        empList.add(zhangSan);
        empList.add(liSi);
        empList.add(wangWu);

        return empList;
    }

}

// Employee及其两个子类是没有任何变化的

代码如下:

public interface IVisitor {

    // 定义可以访问普通员工
    void visit(CommonEmployee commonEmployee);

    // 定义可以访问部门经理
    void visit(Manager manager);


}

public interface IShowVisitor extends IVisitor {

    // 展示报表
    void report();

}

public interface ITotalVisitor extends IVisitor {

    // 统计所有员工工资总和
    void totalSalary();

}

public class ShowVisitor implements IShowVisitor {

    private String info = "";

    // 组装基本信息
    private String getBasicInfo(Employee employee) {
        return "姓名: " + employee.getName() + "\t性别: " +
                (employee.getSex() == Employee.FEMALE ? "女" : "男") + "\t薪水: " +
                employee.getSalary() + "\t";
    }

    @Override
    public void report() {
        System.out.println(info);
    }

    @Override
    public void visit(CommonEmployee commonEmployee) {
        this.info += getBasicInfo(commonEmployee) + "工作: " + commonEmployee.getJob() + "\t\n";
    }

    @Override
    public void visit(Manager manager) {
        this.info += getBasicInfo(manager) + "业绩: " + manager.getPerformance() + "\t\n";
    }

}

public class TotalVisitor implements ITotalVisitor {

    // 部门经理的工资系数是5
    private static final int MANAGER_COEFFICIENT = 5;
    // 员工的工资系数是2
    private static final int COMMON_EMPLOYEE_COEFFICIENT = 2;

    // 普通员工的工资总和
    private int commonTotalSalary = 0;
    // 部门经理的工资总和
    private int managerTotalSalary = 0;

    // 计算部门经理的工资总和
    private void calManagerSalary(int salary) {
        this.managerTotalSalary += salary * MANAGER_COEFFICIENT;
    }

    // 计算普通员工的工资总和
    private void calCommonSalary(int salary) {
        this.commonTotalSalary += salary * COMMON_EMPLOYEE_COEFFICIENT;
    }

    @Override
    public void totalSalary() {
        System.out.println("总工资: " + (managerTotalSalary + commonTotalSalary));
    }

    @Override
    public void visit(CommonEmployee commonEmployee) {
        // 计算普通员工的薪水总和
        calCommonSalary(commonEmployee.getSalary());
    }

    @Override
    public void visit(Manager manager) {
        // 计算部门经理的薪水总和
        calManagerSalary(manager.getSalary());
    }

}

public class Client {

    public static void main(String[] args) {
        IShowVisitor showVisitor = new ShowVisitor();
        ITotalVisitor totalVisitor = new TotalVisitor();
        for (Employee employee : mockEmployee()) {
            employee.accept(showVisitor);
            employee.accept(totalVisitor);
        }
        showVisitor.report();
        totalVisitor.totalSalary();
    }

    public static List<Employee> mockEmployee() {
        ......
    }

}

// Employee及其两个子类是没有任何变化的

着是不是和访问者模式的通用类图很类似?两个accept()方法,其中参数为List类型的则实现了拦截器栈的作用,DynamicProxy类使用了动态代理和反射模式。拦截器实现起来也不复杂,今天就不实现了,这个作为作业,请大家自己来实现。计划在混编模式中一起探讨。

本文原书:

《您的设计模式》 作者:CBF4LIFE

上一篇 下一篇

猜你喜欢

热点阅读