版权声明:转载必须注明本文转自晓_晨的博客:http://blog.csdn.net/niunai112
目录
导航
设计模式之六大设计原则
设计模式(一)单例模式
设计模式(二)工厂模式
设计模式(三)策略模式
设计模式(四)适配器模式
设计模式(五)享元模式
设计模式(六)建造者模式
设计模式(七)原型模式
设计模式(八)桥接模式
设计模式(九)外观模式
设计模式(十)组合模式
设计模式(十一)装饰器模式
设计模式(十二)代理模式
设计模式(十三)迭代器模式
设计模式(十四)观察者模式
设计模式(十五)中介者模式
设计模式(十六)命令模式
设计模式(十七)状态模式
设计模式(十八)访问者模式
设计模式(十九)责任链模式
设计模式(二十)解释器模式
设计模式(二十一)备忘录模式
设计模式(二十二)模板模式
设计模式总结篇(为什么要学习设计模式,学习设计模式的好处)
前言
访问者模式是23个设计模式比较复杂的一个模式,在LZ看来应该是仅次于解释器模式的一种设计模式了。这个模式,将访问数据的过程分为,访问者,被访问物,结构整合类,三个对象。解耦了访问过程。当被访问物是恒定的,不需要改动的情况下,使用访问者模式比较有优势,否则不要使用访问者模式。
访问者(Visitor):接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。
具体访问者(ConcreteVisitor):具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。
被访问元素(Element):元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
具体被访问元素(ConcreteElementA):具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
结构类(ObjectStructure):定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。
例子
一个抽象实现例子,想要理解例子,LZ还得插个嘴,给介绍一下分派的概念,
静态分派:就是按照变量的类型进行分派,确定究竟用哪个方法执行,静态分派在编译期确定方法的使用版本。
动态分派:动态分派在运行期确定方法使用的版本,就是说只有在运行时,根据实际类的类型来确定使用的方法。
这两个概念在下面的例子中都有体现。
/***
*
*@Author ChenjunWang
*@Description:具体访问物接口
*@Date: Created in 17:45 2018/4/12
*@Modified By:
*
*/
public interface Node {
//这里是动态分派,接口类不好体现。我们看下结构类
void accept(Visitor visitor);
}
/***
*
*@Author ChenjunWang
*@Description:具体访问物类A
*@Date: Created in 18:29 2018/4/12
*@Modified By:
*
*/
public class ConcreteNodeA implements Node{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/***
*
*@Author ChenjunWang
*@Description:具体访问物类B
*@Date: Created in 18:29 2018/4/12
*@Modified By:
*
*/
public class ConcreteNodeB implements Node{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/***
*
*@Author ChenjunWang
*@Description:具体访问者接口
*@Date: Created in 17:31 2018/4/12
*@Modified By:
*
*/
public interface Visitor {
//这里用到了静态分派,在编译期就能确定,ConcreteNodeA参数走visit(ConcreteNodeA concreteNodeA)方法,
//ConcreteNodeB参数走visit(ConcreteNodeA concreteNodeB)方法。利用方法的重载
void visit(ConcreteNodeA concreteNodeA);
void visit(ConcreteNodeB concreteNodeB);
}
/***
*
*@Author ChenjunWang
*@Description:具体访问者B
*@Date: Created in 17:45 2018/4/12
*@Modified By:
*
*/
public class ConcreteVisitorA implements Visitor{
@Override
public void visit(ConcreteNodeA concreteNodeA) {
//具体处理过程写这里面
System.out.println("ConcreteVisitorA 处理 concreteNodeA");
}
@Override
public void visit(ConcreteNodeB concreteNodeB) {
//具体处理过程写这里面
System.out.println("ConcreteVisitorA 处理 concreteNodeB");
}
}
/***
*
*@Author ChenjunWang
*@Description:具体访问者B
*@Date: Created in 17:45 2018/4/12
*@Modified By:
*
*/
public class ConcreteVisitorB implements Visitor{
@Override
public void visit(ConcreteNodeA concreteNodeA) {
//具体处理过程写这里面
System.out.println("ConcreteVisitorB 处理 concreteNodeA");
}
@Override
public void visit(ConcreteNodeB concreteNodeB) {
//具体处理过程写这里面
System.out.println("ConcreteVisitorB 处理 concreteNodeB");
}
}
/***
*
*@Author ChenjunWang
*@Description:结构类
*@Date: Created in 18:30 2018/4/12
*@Modified By:
*
*/
public class ObjectStructure {
private List<Node> list = new ArrayList<>();
public void action(Visitor visitor){
for (Node n : list){
//看完LZ的例子后再来看这里,你会知道visit是有2个方法的,一个参数是ConcreteNodeA,另一个是ConcreteNodeB。
//在编译期,肯定不能确定这个Node是那个具体的类型,只有到运行期间,代码走到这,才知道具体的类型,
// 假如是ConcreteNodeA类型,走visit(ConcreteNodeA concreteNodeA);假如是ConcreteNodeB类型,走visit(ConcreteNodeA concreteNodeB),
// 所以这个是动态分派的地方。主要是利用多态特性
n.accept(visitor);
}
}
public void add(Node node){
list.add(node);
}
}
/***
*
*@Author ChenjunWang
*@Description:测试类
*@Date: Created in 18:30 2018/4/12
*@Modified By:
*
*/
public class Test {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
ConcreteNodeA concreteNodeA = new ConcreteNodeA();
ConcreteNodeB concreteNodeB = new ConcreteNodeB();
objectStructure.add(concreteNodeA);
objectStructure.add(concreteNodeB);
System.out.println("------------------访问者A访问元素------------------");
objectStructure.action(new ConcreteVisitorA());
System.out.println("------------------访问者B访问元素------------------");
objectStructure.action(new ConcreteVisitorB());
}
}
运行结果如下
----------------------------------------------------
------------------访问者A访问元素------------------
ConcreteVisitorA 处理 concreteNodeA
ConcreteVisitorA 处理 concreteNodeB
------------------访问者B访问元素------------------
ConcreteVisitorB 处理 concreteNodeA
ConcreteVisitorB 处理 concreteNodeB
总结
优点
(1)解耦访问者与访问物的之间的关系。
(2)要增加访问者非常简单,只需要增加一个访问者类,实现对各个对访问物的操作方法即可。
缺点
(1)增加访问物非常麻烦,要修改所有的访问者,破坏了开闭原则。
(2)系统的复杂程度也会大大提升。
Git地址
本篇实例Github地址:https://github.com/stackisok/Design-Pattern/tree/master/src/visitor
有什么不懂或者不对的地方,欢迎留言。
喜欢LZ文章的小伙伴们,可以关注一波,也可以留言,LZ会回你们的。
觉得写得不错的小伙伴,欢迎转载,但请附上原文地址,谢谢^_^!