版权声明:本文为博主原创文章,未经博主允许不得转载。转载请注明出处,并告知本人 https://blog.csdn.net/the_conquer_zzy/article/details/83628285
访问者模式
访问者模式的场景:
公司老板要看报表, 公司有销售,财务,研发,每个员工有自己独特的信息,最常见的做法是: 定义抽象员工,并定义模板方法,每一个员工实现自己角色的个性化方法,通过模板方法report来访问。
比如是这样的,
public abstract Employee{
private String name;
private String telephone;
public String getBasicInfo(){
return name+"tel: "+telephone;
}
public abstract String getSpecialInfo();
public void report(){
System.out.println(getBasicInfo());
System.out.println(getSpecialInfo());
}
}
这样每个员工继承基类,各自实现自己的特殊信息就可以了。
但是这样有一个问题,报表的输出格式全由report() 方法控制, 如果有一天老板想换种报表格式,比如不想看员工姓名了怎么办?
是不是要修改基类的report方法了, 开闭原则指导要对修改关闭对扩展开放, 如果直接改基类report也会出现问题,比如今天一个格式出报表,改下基类,明天又换个格式改报表,又要改基类,还想同时出两份格式不同的报表怎么办?
所以就引出本文中提到的访问者模式。
首先分析,要为每一个角色个性化自己的报表方法,可以把角色作为参数传给一个单独的访问者对象Visitor, 由Visitor利用多态来实现个性化输出。并且由Visitor 去负责产生报表格式。 对象和Visitor是弱耦合的,不是继承关系。 这种实现方式便于扩展,需要产生不同格式的报表,多添加一个Visitor完成这个特殊的功能就可以了。
访问者模式的角色
-
Visitor 抽象访问者。 声明哪些元素可以访问
-
ConcreteVisitor 具体访问者。 访问到一个类后,具体怎么做,做什么
-
Element 抽象元素。 接口或者抽象类,声明接受哪一类访问者访问。程序中通过accept方法中的参数来定义
-
ConcreteElement 具体元素,实现accept方法
-
ObjectStructure 结构对象。 元素产生者
public abstract class Element { public abstract void doSomething(); public abstract void accept(IVisitor visitor); } public class ConcreteElement1 extends Element{ @Override public void doSomething() { //业务处理 } //允许哪个访问者访问 @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public class ConcreteElement2 extends Element{ @Override public void doSomething() { //业务处理 } //允许哪个访问者访问 @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public interface IVisitor{ public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); } public class Visitor implements IVisitor{ @Override public void visit(ConcreteElement1 el1) { el1.doSomething(); } @Override public void visit(ConcreteElement2 el2) { el2.doSomething(); } } public class ObjectStructure{ public static Element createElement(){ Random rand=new Random(); if(rand.nextInt(100)>50){ return new ConcreteElement1(); }else{ return new ConcreteElement2(); } } } public class Client{ public static void main(String[] args){ for(int i=0;i<10;i++){ Element e=ObjectStructure.createElement(); e.accept(new Visitor()); } } }
访问者模式优点
- 符合单一原则。 Visitor负责报表的实现,Employee及其子类负责数据加载
- 优秀的扩展性。 如果需要不同的格式报表,直接新增Visitor或者在Visitor新增方法就可以了。
- 灵活性高。
访问者模式的缺点
- 具体元素对访问者公布细节。 违反迪米特法则
- 具体元素变更比较困难
- 违背了依赖倒置原则。 放弃接口,依赖实现类,扩展比较困难。
访问者模式的使用场景
- 需要遍历不同的对象, 迭代器模式只能访问同类或同接口的数据,访问者模式是对迭代器模式的 扩充,可以根据不同的对象,执行不同的操作。