访问者模式 用的不多 但是思想还是值得学习的
? 对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型
,而且对于该集合中的对象,可以接受类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同
,访问者模式为解决这类问题而诞生。
模式动机的模式动机其实是比较容易理解的,在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式
, 而且这些操作方式并不稳定
,可能还需要增加新的操作
,以满足新的业务需求.此时,访问者模式就是一个值得考虑的解决方案。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。
? 访问者模式( Visitor pattern)定义是这样的:表示一个作用于某对象结构中的各元素的操作
,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作
。访问者模式是一种对象行为型
模式。
? 访问者模式包含如下角色:
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
? 下面用代码演示一下:(进行了简化,突出本质)
创建访问者的接口( 抽象访问者Visitor)
public interface IVisitor {
void visit(FreeCourse freeCourse);
void visit(CodingCourse codingCourse);
}
创建访问者(具体访问者ConcreteVisitor)
public class Visitor implements IVisitor {
//访问免费课程,打印所有免费课程名称
@Override
public void visit(FreeCourse freeCourse) {
System.out.println("免费课程:"+freeCourse.getName());
}
//访问编程课程,打印所有编程课程名称及价格
@Override
public void visit(CodingCourse codingCourse) {
System.out.println("编程课程:"+codingCourse.getName()+" 价格:"+codingCourse.getPrice()+"元");
}
}
创建抽象类 课程(抽象元素Element)
课程里面有定义访问者的访问相关行为的抽象方法
public abstract class Course {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//处理访问者的访问 参数是访问者
public abstract void accept(IVisitor visitor);
}
编码课程( 具体元素ConcreteElement)
public class CodingCourse extends Course {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
免费课程( 具体元素ConcreteElement)
public class FreeCourse extends Course {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
测试类
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Course> courseList = new ArrayList<Course>();
FreeCourse freeCourse = new FreeCourse();
freeCourse.setName("操作系统");
CodingCourse codingCourse = new CodingCourse();
codingCourse.setName("算法");
codingCourse.setPrice(299);
courseList.add(freeCourse);
courseList.add(codingCourse);
for(Course course : courseList){
course.accept(new Visitor());
}
}
}
运行结果:
类图
换一种角度
通过访问者接口 它们联系在了一起
? 模式分析
访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。(这里没有展现出来,角色太少了…)
访问者模式包括两个层次结构,一个是访问者层次结构
,提供了抽象访问者和具体访问者,一个是元素层次结构
,提供了抽象元素和具体元素。
相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。
在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。
? 模式优缺点
访问者模式的优点有:
- 使得
增加新的访问操作变得很容易
。 将有关元素对象的访问行为集中到一个访问者对象中
,而不是分散到一个个的元素类中- 可以
跨过类的等级结构访问属于不同的等级结构的元素类
- 让用户能够在
不修改现有类层次结构
的情况下,定义该类层次结构的操作
。
访问者模式的缺点
- 访问者模式的缺点增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
? 在以下情况下可以使用访问者模式:
- 一个对象结构包含很多类型的对象,
希望对这些对象实施一些依赖其具体类型的操作
。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作 - 需要
对一个对象结构中的对象进行很多不同的并且不相关的操作
,而需要避免让这些操作“污染”这些对象的类
,也不希望在增加新操作时修改这些类
。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。 - 对象结构中
对象对应的类很少改变
,但经常需要在此对象结构上定义新的操作。
? 模式应用
在一些编译器的设计中运用了访问者模式,程序代码是被访问的对象,它包括变量定义、变量赋值、逻辑运算、算术运算等语句,编译器需要对代码进行分析,如检査变量是否定义、变量是否赋值、算术运算是否合法等,可以将不同的操作封装在不同的类中,如检査变量定义的类、检査变量赋值的类、检査算术运算是否合法的类,这些类就是具体访问者,可以访问程序代码中不同类型的语句。在编译过程中除了代码分析外,还包含代码优化、空间分配和代码生成等部分,也可以将每一个不同编译阶段的操作封装到了跟该阶段有关的一个访问者类中。
? 模式扩展
-
由于访问者模式需要对对象结构进行操作,而对象结构本身是一个元素对象的集合,因此访问者模式经常需要与
迭代器模式
联用,在对象结构中使用迭代器来遍历元素对象。 -
在访问者模式中,元素对象可能存在容器对象和叶子对象,因此可以结合
组合模式
来进行设计。 -
访问者模式以一种倾斜的方式支持“开闭原则”,增加新的访问者方便,但是增加新的元素很困难。