设计模式之罪 2-19 访问者模式

一、定义

访问者模式(Visitor Pattern)的定义如下:
Represent an operation to be performed on the element of an object structure. Visitor lets you define a new operation without changing the classes of elements on which it operates.(封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作)

访问者模式的通用类图如上所示。由图可知,状态模式中有5个角色:

  • Visitor - 抽象访问者:接口或抽象类,声明访问者可以访问哪些元素。
  • ConcreteVisitor - 具体访问者:它影响到访问者访问到一个类后该怎么干。
  • Element - 抽象元素:接口或抽象类,声明接受哪一类访问者访问。
  • ConcreteElement - 具体元素:实现accept方法,通常是visitor.visit(this)
  • ObjectStructure - 结构对象:元素生产者,一般容纳在多个不同类、不同接口的容器中,例如List、Set、Map等。

状态模式的核心:封装。状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变。

二、通用代码

1. 抽象元素

public abstract class Element{
    //业务逻辑
    public abstract void doSomething();
    //允许谁来访问
    public abstract void accept(IVisitor visitor);
}

抽象元素有2类方法:一是本身的业务逻辑;二是允许哪一个访问者来访问。

2. 具体元素

public class ConcreteElement1 extends Element {
    //完善业务逻辑
    public void handle1() {
        //业务处理
    }
    //处理哪个访问者访问
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}
public class ConcreteElement2 extends Element {
    //完善业务逻辑
    public void handle1() {
        //业务处理
    }
    //处理哪个访问者访问
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

这里定义了2个具体元素。

3. 抽象访问者

public interface IVisitor {
    //可以访问哪些对象
    public void visit(ConcreteElement1 el1);
    public void visit(ConcreteElement2 el2);
}

一般来说,有几个具体元素,抽象访问者就有几个访问方法

4. 具体访问者

public class Visitor implements IVisitor {
    //可以el1对象
    public void visit(ConcreteElement1 el1) {
		el1.doSomething();
	}
    //可以el2对象
    public void visit(ConcreteElement2 el2) {
		el2.doSomething();
	}
}

一般来说,有几个具体元素,抽象访问者就有几个访问方法

5. 结构对象

结构对象是产生出不同的元素对象,我们使用工厂方法模式来模拟,如下:

public class ObjectStructure {
    public static Element createElement() {
		Random rand = new Random();
		if (rand.nextInt(100) > 50) {
			return new ConcreteElement1();
		}else {
			return new COncreteElement2();
		}
	}
}

6. 场景类

public class Client {
    public static void main(String[] args) {
    	//定义环境角色
    	for (int i=0; i<10; i++) {
			//获得元素对象
			Element e1 = ObjectStructure.createElement();
			//接受访问者访问
			e1.accept(new Visitor());
		}
    }
}

三、实际例子

员工定制报表。

1. 抽象元素

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;

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

2. 具体元素

/**
 * 普通员工
 */
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);
    }
}

3. 访问者接口

public interface IVisitor {
    //访问普通员工
    public void visit(CommonEmployee commonEmployee);
    //访问部门经理
    public void visit(Manager manager);
}

4. 访问者实现

public class Visitor implements IVisitor {
    @Override
    public void visit(CommonEmployee commonEmployee) {
        System.out.println(this.getCommonEmployee(commonEmployee));
    }

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

5. 场景类

public class Client {
    public static void main(String[] args) {
        for (Employee emp:mockEmployee()) {
            emp.accept(new Visitor());
        }
    }

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

        CommonEmployee zhangSan = new CommonEmployee();
        zhangSan.setJob("编写Java程序,绝对的蓝领,苦工加搬运工");
        zhangSan.setName("张三");
        zhangSan.setSex(Employee.MALE);
        zhangSan.setSalary(1900);
        empList.add(zhangSan);

        CommonEmployee liSi = new CommonEmployee();
        liSi.setJob("页面美工,审美素质太不流行了");
        liSi.setName("李四");
        liSi.setSex(Employee.FEMALE);
        liSi.setSalary(1800);
        empList.add(liSi);

        Manager wangWu = new Manager();
        wangWu.setName("王五");
        wangWu.setPerformance("基本上是负值,但是我会拍马屁呀");
        wangWu.setSex(Employee.MALE);
        wangWu.setSalary(13000);
        empList.add(wangWu);

        return empList;
    }
}

执行结果如下:

姓名:张三	性别男	薪水1900	工作:编写Java程序,绝对的蓝领,苦工加搬运工	
姓名:李四	性别女	薪水1800	工作:页面美工,审美素质太不流行了	
姓名:王五	性别男	薪水13000	业绩:基本上是负值,但是我会拍马屁呀

当需要改变报表时,仅需新增访问者的实现类。

四、访问者模式的应用

1. 访问模式的优点和缺点

优点:符合单一职责原则、扩展性高和灵活性高。
缺点:具体元素对访问者公开,具体元素变更困难,违背依赖倒置原则。

2. 访问者模式的使用场景

  • 一个对象结构包含很多类对象,它们有不同的接口,而需要对这些对象实施一些依赖于其具体类的操作中,也就是迭代器模式不能胜任的情景。
  • 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而想要避免这些操作“污染”这些对象的类。

也就是说需要遍历不同的对象,执行不同操作的情景

3. 最佳实践

是一种集中规整的模式,适合大规模重构的项目,在这一阶段,功能点明确,可以很容易将一些功能进行梳理。此外,还可以与其他模式混编建立拦截器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值