访问者模式
现在有一组结构固定的类,现在有多个业务,都是为了拿到这些类里面的信息然后再处理。由于不同的业务要拿到不同的信息。传统的做法就是依靠if-else分支来实现。这种方法在业务越来越多的时候将不利于维护。因此设计者模式便出现了。
这里记录书上的一个例子来说明。
公司高层在年终的时候对公司的员工进行年终奖考核,假设公司的高层只有CEO和CTO,员工只有工程师(engineer)和经理(manager)。CEO考评员工时,如果这个员工是工程师(engineer),就只关注他的KPI值;如果这个员工是经理(manager),就要关注他的KPI值和新产品的数量。CTO在考评员工时,如果这个员工是工程师(engineer),就只关注他的代码数量,如果这个员工是经理(manager),就只关注他的新产品的数量。
在这个例子中,CEO和CTO是充当的是访问者(visitor)的角色,而工程师(engineer)和经理(manager)充当的是元素(element)的角色。使用访问者模式实现这个业务需求分为以下步骤
一、新建Element基类
public abstract class Element {
private String kpi;
private String name;
public Element(String name) {
this.kpi =String.valueOf(new Random().nextInt(100));
this.name = name;
}
public String getKpi() {
return kpi;
}
public String getName() {
return name;
}
abstract void accept(Visitor visitor);
}
这个类是所有员工类的基类,在这里设置里面包含一些公共的属性和方法,以及一个accept方法,这个方法具体的逻辑由员工的子类实现。
二、新建ManagerElement类
public class ManagerElement extends Element {
private String productNum;
public ManagerElement(String name) {
super(name);
this.productNum=String.valueOf(new Random().nextInt(20));
}
public String getProductNum() {
return productNum;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
这个类是经理(manager)的Element类的具体实现,在这里面实现了基类里面的accept方法的具体逻辑。调用的是visitor的visit方法(定义在后面)。
三、新建EngineerElement类
public class EngineerElement extends Element {
private String codeLine;
public EngineerElement(String name) {
super(name);
this.codeLine=String.valueOf(new Random().nextInt(10000));
}
public String getCodeLine() {
return codeLine;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
这个类是工程师(engineer)的Element类的具体实现,在这里面实现了基类里面的accept方法的具体逻辑。调用的是visitor的visit方法(定义在后面)。
四、新建Visitor类
public abstract class Visitor {
abstract public void visit(EngineerElement engineer);
abstract public void visit(ManagerElement manager);
}
这个类是所有Visitor类的基类,这里面定义了visit方法对不同的element的访问
五、新建CEOVisitor类
public class CEOVisitor extends Visitor{
public void visit(EngineerElement engineer) {
System.out.println(engineer.getName()+"----kpi:"+engineer.getKpi());
}
public void visit(ManagerElement manager) {
System.out.println(manager.getName()+"----kpi:"+manager.getKpi()+",产品数量:"+manager.getProductNum());
}
}
这个类是CEO对visitor的实现,对两个不同element访问方法具体的逻辑就是在这里面实现的。
六、新建CEOVisitor类
public class CTOVisitor extends Visitor{
public void visit(EngineerElement engineer) {
System.out.println(engineer.getName()+"----:代码量:"+engineer.getCodeLine());
}
public void visit(ManagerElement manager) {
System.out.println(manager.getName()+"----产品数量:"+manager.getProductNum());
}
}
这个类是CTO对visitor的实现,对两个不同element访问方法具体的逻辑就是在这里面实现的。
七、新建Structure类
public class Structure {
private List<Element> elementList;
public Structure(){
elementList=new ArrayList<Element>();
elementList.add(new EngineerElement("工程师1"));
elementList.add(new EngineerElement("工程师2"));
elementList.add(new ManagerElement("经理1"));
elementList.add(new ManagerElement("经理2"));
}
public void showResulTo(Visitor visitor){
for(Element element:elementList){
element.accept(visitor);
}
}
}
这里是visitor访问elements的桥梁,在其内部维护了一个element集合,并提供一个访问者访问这个集合的方法。通过遍历这个element集合并调用每一个element的accept方法实现访问者对每一个element的访问。
八、新建Client类
public class Client {
public static void main(String[] args){
Structure structure=new Structure();
System.out.println("------CEO的报表------");
structure.showResulTo(new CEOVisitor());
System.out.println("------CTO的报表------");
structure.showResulTo(new CTOVisitor());
}
}
这个是测试类,在这里面new一个Structure 对象structure,并通过structure的showResulTo()方法实现不同访问者对element的访问。
现在run一下main方法,得到下面的输出
"C:\Program Files\Java\jdk-10.0.2\bin\java.exe" "-javaagent:E:\IntelliJ IDEA\IntelliJ IDEA 2018.3.5\lib\idea_rt.jar=22400:E:\IntelliJ IDEA\IntelliJ IDEA 2018.3.5\bin" -Dfile.encoding=UTF-8 -classpath F:\program\Java\myProject\VisitorTest\target\classes com.xiaogege.Client
------CEO------
工程师1----kpi:15
工程师2----kpi:91
经理1----kpi:6,产品数量:10
经理2----kpi:49,产品数量:13
------CTO------
工程师1----:代码量:7746
工程师2----:代码量:245
经理1----产品数量:10
经理2----产品数量:13
Process finished with exit code 0
至此一个完整的访问者模式的使用实例就完成了。
至于访问者模式的理论以及使用的场景以及优缺点等等的理论就不记录了,百度,Google上都有相关内容。
参考书籍《Android源码设计模式解析与实战 第二版》