概念:
Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作。
访问者模式的应用示例
比如有一个公园,有一到多个不同的组成部分;该公园存在多个访问者:清洁工A负责打扫公园的A部分,清洁工B负责打扫公园的B部分,公园的管理者负责检点各项事务是否完成,上级领导可以视察公园等等。也就是说,对于同一个公园,不同的访问者有不同的行为操作,而且访问者的种类也可能需要根据时间的推移而变化(行为的扩展性)。根据软件设计的开闭原则(对修改关闭,对扩展开放),我们怎么样实现这种需求呢?
访问者模式的结构
访问者模式的角色和职责
1、 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
2、具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。
3、元素角色(Element):定义一个Accept操作,它以一个访问者为参数。
4、具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
5、对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
下面,用代码来实现一下访问者模式
首先,先新建一个公园的各个组成部分(因为有了各个组成部分才能有公园,所以我们先建各个组成部分,再建公园)
新建一个公园各个部分的抽象类(元素角色Element),需要传入一个访问者,一会后面会创建访问者
1 /* 2 * 公园每一部分的抽象 3 */ 4 public interface ParkElement { 5 //用来接纳访问者 6 public void accept(Visitor visitor); 7 }
再新建公园的各个部分,这是具体元素角色(Concrete Element)
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
1 public class ParkA implements ParkElement{ 2 3 @Override 4 public void accept(Visitor visitor) { 5 visitor.visit(this); 6 } 7 8 }
1 public class ParkB implements ParkElement{ 2 3 @Override 4 public void accept(Visitor visitor) { 5 visitor.visit(this); 6 } 7 8 }
有了公园的各个部分,我们就可以开始新建公园了,这就是对象结构角色(Object Structure)
1 /* 2 * 整个公园,其中包含了公园的各个部分 3 */ 4 public class Park implements ParkElement{ 5 private ParkA parkA; 6 private ParkB parkB; 7 8 public Park() { 9 super(); 10 this.parkA = new ParkA(); 11 this.parkB = new ParkB(); 12 } 13 14 @Override 15 public void accept(Visitor visitor) { 16 visitor.visit(this); 17 parkA.accept(visitor); 18 parkB.accept(visitor); 19 } 20 21 }
有了公园,我们就可以开始建造访问者了,先建造访问者角色(Visitor)
1 /* 2 * 其中包含访问公园不同区域的重载方法,其中重载的参数Park,ParkA,PArkB,不仅可以获得公园的信息,还用来作为重载的条件 3 */ 4 public interface Visitor { 5 public void visit(Park park); 6 public void visit(ParkA parkA); 7 public void visit(ParkB parkB); 8 }
然后在建造具体访问者角色,就是具体访问者角色(Concrete Visitor)
1 /* 2 * 清洁工A负责公园A的清洁情况 3 */ 4 public class VisitorA implements Visitor{ 5 6 @Override 7 public void visit(Park park) { 8 } 9 10 @Override 11 public void visit(ParkA parkA) { 12 System.out.println("清洁工A完成了公园A的卫生"); 13 } 14 15 @Override 16 public void visit(ParkB parkB) { 17 } 18 19 }
1 /* 2 * 清洁工B负责公园B的清洁情况 3 */ 4 public class VisitorB implements Visitor{ 5 6 @Override 7 public void visit(Park park) { 8 } 9 10 @Override 11 public void visit(ParkA parkA) { 12 } 13 14 @Override 15 public void visit(ParkB parkB) { 16 System.out.println("清洁工B完成了公园B的卫生"); 17 } 18 19 }
1 /* 2 * 公园管理员负责检查整个公园的卫生情况 3 */ 4 public class VisitorManager implements Visitor{ 5 6 @Override 7 public void visit(Park park) { 8 System.out.println("管理员:负责公园的卫生检查"); 9 } 10 11 @Override 12 public void visit(ParkA parkA) { 13 System.out.println("管理员:负责公园A部分的卫生检查"); 14 } 15 16 @Override 17 public void visit(ParkB parkB) { 18 System.out.println("管理员:负责公园B部分的卫生检查"); 19 } 20 21 }
最后,新建一个客户端
1 public class MainClass { 2 public static void main(String[] args) { 3 Park park = new Park(); 4 5 Visitor visitorA = new VisitorA(); 6 park.accept(visitorA); 7 8 Visitor visitorB = new VisitorB(); 9 park.accept(visitorB); 10 11 VisitorManager visitorManager = new VisitorManager(); 12 park.accept(visitorManager); 13 } 14 }
结果:
这样,一个访问者模式的例子就完成了,一定把上面的程序看懂
访问者模式的使用意图:
主要将数据结构与数据操作分离,解决了稳定的数据结构和易变的操作耦合问题。
访问者模式的使用场景:
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
访问者模式的优缺点:
优点:
1、符合单一职责原则。
2、优秀的扩展性。
3、灵活性。
缺点:
1、具体元素对访问者公布细节,违反了迪米特原则。
2、具体元素变更比较困难。
3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
注意事项:
访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。