author zong
email zongzhe1996@163.com
介绍
在访问者模式中,通过使用一个访问者类,可以改变元素类(被访问者)的执行算法。元素类的执行算法可以随着访问者的改变而改变 。使用访问者对象操作元素对象,不同的访问者可以灵活调整对元素对象的操作。
解决问题
主要解决了数据结构与数据操作的耦合问题,使得数据结构与数据的操作分离,可以灵活的调整针对同一个数据结构不同操作。
使用场景
一个对象结构需要面临很多不同的操作,并且这些操作间互不相关,在设计过程中需要避免让一个对象中包含多个与对象内容无关的操作,遵守单一职责原则。
解决思路
在被访问的类中添加一个用来接待访问者的接口,在这个接口中,被访问的类将自己的引用传递给访问者。
具体实现
本节给出一种访问者模式最常用的设计方案,主要结构如下所示:
首先,创建一个元素类(被访问者)的抽象接口。
public interface VisitedPart{
//用来接待访问者的接口
public void accept(VistorPart vp);
}
其次,创建上述抽象接口的实体类。
public class VisitedPart1 implements VisitedPart{
public void accept(VistorPart vp){
//访问者在这个接口中对this引用指向的对象进行访问。
vp.visit(this)
}
}
public class VisitedPart2 implements VisitedPart{
public void accept(VistorPart vp){
vp.visit(this)
}
}
public class VisitedPart3 implements VisitedPart{
public void accept(VistorPart vp){
vp.visit(this)
}
}
public class TopVisitedPart1 implements VisitedPart{
VisitedPart[] parts;
public TopTopVisitedPart1(){
parts = new VisitedPart[]{
new VisitedPart1(),
new VisitedPart2(),
new VisitedPart3()};
}
public void accept(VistorPart vp){
for (VisitedPart vedp in parts){
vedp.accept(vp);
}
vp.visit(this)
}
}
然后,定义访问者的抽象接口。
public interface VisitorPart{
public void visit(VisitedPart1 vedp1);
public void visit(VisitedPart2 vedp2);
public void visit(VisitedPart3 vedp3);
public void visit(TopVisitedPart1 vedp4);
}
对访问者的操作进行具体实现。
public class ConcreteVisitor implements VisitorPart{
public void visit(VisitedPart1 vedp1){
sout("Visit One");
}
public void visit(VisitedPart2 vedp2){
sout("Visit Two");
}
public void visit(VisitedPart3 vedp3){
sout("Visit Three");
}
public void visit(TopVisitedPart1 vedp4){
sout("Visit Top");
}
}
最后,在主函数中展示使用过程。
void main(String[] args) {
VisitedPart vedp = new TopVisitedPart1();
vedp.accept(new ConcreteVisitor());
}
缺点
具体元素对访问者公布细节,违反了迪米特原则。
具体元素变更比较困难。
违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
总结
作者最初以为访问者模式还违反了开闭原则,因为添加新的被访问者需要在 VisitorPart
和 ConcreteVisitor
中增加新的方法。但是后来仔细想想好像不用,可以创建一个新接口 VisitorNew
继承 VisitorPart
并添加适用于新的被访问者的方法,然后在 VisitorNew
的实现类中实现新增加的方法即可。对于 ConcreteVisitor
已经实现过的方法不用再实现。访问原有被访问者就仍然使用 ConcreteVisitor
,需要访问新的被访问者的时候再用 VisitorNew
的实现类。
但是这样有个问题,如果多次调整添加了很多被访问者的话,就会产生大量的接口继承和多层继承结构,会使程序结构变得复杂,到这里作者设计模式还没有学完,后面学完后可能会有新的设计思路,这里暂时先提出一种改进思路。将上述实现过程中的访问者接口( VisitorPart
)中的四个访问方法下放到实现类中。将 VisitorPart
进一步抽象。
public interface VisitorPart{
//访问者方法的参数抽象为被访问者的抽象接口
public void visit(VisitedPart vedp);
}
并将不同的访问方法下放到实现类中实现
public class ConcreteVisitor1 implements VisitorPart{
public void visit(VisitedPart vedp){
//由于需要进行强制类型转换,因此需要进行异常处理。
VisitedPart1 vedp1 = (VisitedPart1) vedp
sout("Visit One");
}
}
public class ConcreteVisitor2 implements VisitorPart{
public void visit(VisitedPart vedp){
//由于需要进行强制类型转换,因此需要进行异常处理。
VisitedPart2 vedp2 = (VisitedPart2) vedp
sout("Visit Two");
}
}
public class ConcreteVisitor3 implements VisitorPart{
public void visit(VisitedPart vedp){
//由于需要进行强制类型转换,因此需要进行异常处理。
VisitedPart1 vedp3 = (VisitedPart3) vedp
sout("Visit Three");
}
}
public class TopPartVisitor implements VisitorPart{
public void visit(VisitedPart vedp){
//由于需要进行强制类型转换,因此需要进行异常处理。
TopVisitedPart1 tvedp = (TopVisitedPart1) vedp
sout("Visit Top");
}
}
最后,在主函数中展示使用过程。
void main(String[] args) {
VisitedPart vedp = new TopVisitedPart1();
vedp.accept(new TopPartVisitor());
}
这样调整后,如果添加了新的被访问者,就直接添加一个 VisitorPart
的实现类。