访问者模式


23种设计模式

1.访问者模式解决的问题

首先看这样的一个情景:
西游记师徒四人,一路十万八千里。每日都需要化缘才能有吃的。
大师兄本领强大,直接去找吃的,不给就硬抢。
猪八戒好吃懒作,会去偷吃的。
沙和尚为人老实,会用自己的力气化缘。
至于唐僧则是给人讲佛法,以此来化缘。

所以,我们可以发现:对于同一件事情,或者说同一个事物,师徒四人用自己独特的方式达到目的。

对于食物,就是访问者模式中基本的数据,但是处理这个数据有着许多的方式,为了将数据与操作分离,或者为了更加方便的增加访问数据的方式,访问者模式能够很好的解决这一问题。

2.访问者模式中的角色

抽象的数据accept
具体的数据accept
抽象的访问者visit
具体的访问者visit
访问者数据分配

3.关系


实现

实现

实现

关联

Visitor

AbsVisit

ImplEle1

AbsEle

ImplEle2

Structure


4.例子

仿照化缘的过程,我们做一个简单的例子。

4.1结构图

23种设计模式----访问者模式----行为型模式_数据与操作分离

4.2 抽象数据结构

package visitor.element.abs;

import visitor.visit.abs.Visit;

public abstract class Home {

private String name;

public abstract void accept(Visit visit);

public Home(String name){
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

4.3 实际的数据结构

package visitor.element.impl;

import visitor.element.abs.Home;
import visitor.visit.abs.Visit;

public class Home1 extends Home{

public Home1(String name) {
super(name);
}

@Override
public void accept(Visit visit) {
System.out.println("accept#home1");
visit.visit(this);
}

}
package visitor.element.impl;

import visitor.element.abs.Home;
import visitor.visit.abs.Visit;

public class Home2 extends Home{

public Home2(String name) {
super(name);
}

@Override
public void accept(Visit visit) {
System.out.println("accept#home2");
visit.visit(this);
}

}

4.4 抽象的访问者

package visitor.visit.abs;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;

public interface Visit {

public void visit(Home1 home1);

public void visit(Home2 home2);

}

4.5 实际的访问者

package visitor.visit.impl;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;
import visitor.visit.abs.Visit;

public class Visitor implements Visit{

@Override
public void visit(Home1 home1) {
System.out.println("visit#home1");

}

@Override
public void visit(Home2 home2) {
System.out.println("visit#home2");
}

}
package visitor.visit.impl;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;
import visitor.visit.abs.Visit;

public class Visitor1 implements Visit{

@Override
public void visit(Home1 home1) {
System.out.println("visit&home1");
}

@Override
public void visit(Home2 home2) {
System.out.println("visit&home2");
}

}

4.6 访问者数据分配

package visitor.client;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;
import visitor.visit.impl.Visitor;
import visitor.visit.impl.Visitor1;

public class Main {

public static void main(String[] args) {
new Home1("home1").accept(new Visitor());
new Home2("home2").accept(new Visitor());

new Home1("home1").accept(new Visitor1());
new Home2("home2").accept(new Visitor1());
}

}

4.7 结果

accept#home1
visit#home1
accept#home2
visit#home2
accept#home1
visit&home1
accept#home2
visit&home2

4.8 增加一个访问者

package visitor.visit.impl;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;
import visitor.visit.abs.Visit;

public class Visitor2 implements Visit{

@Override
public void visit(Home1 home1) {
System.out.println("Visitor2%"+home1.getName());
}

@Override
public void visit(Home2 home2) {
System.out.println("Visitor2%"+home2.getName());
}

}

4.9 访问者分配者

package visitor.client;

import visitor.element.impl.Home1;
import visitor.element.impl.Home2;
import visitor.visit.impl.Visitor;
import visitor.visit.impl.Visitor1;
import visitor.visit.impl.Visitor2;

public class Main {

public static void main(String[] args) {
new Home1("home1").accept(new Visitor());
new Home2("home2").accept(new Visitor());

new Home1("home1").accept(new Visitor1());
new Home2("home2").accept(new Visitor1());

new Home1("home1").accept(new Visitor2());
new Home2("home2").accept(new Visitor2());
}

}

4.10 结果

accept#home1
visit#home1
accept#home2
visit#home2
accept#home1
visit&home1
accept#home2
visit&home2
accept#home1
Visitor2%home1
accept#home2
Visitor2%home2

5.知识点

5.1 双重分发

在例子中有两个方法:
visit
accept
其中:
数据.accept(访问者)
访问者.visit(数据)
这两个方法是相反的方法,这就是双重分发。
由数据和访问者共同决定调用实际的方法。

5.2 数据与处理分离

处理与数据分离,那么可以提高组件的复用性,数据是数据,处理是处理。

5.3 开闭原则

对扩展开放,对修改关闭。
对于访问者模式来说,增加一个访问者是一件非常轻松的事情,只需要实现访问者的接口即可。
但是对于数据来说,增加一个数据,则要修改所有的访问者,所以,对于访问者模式,增加操作简单,增加要处理的对象困难。

5.4 扩展

可以使用迭代器模式提供访问者提供数据
以及使用组合模式扩展访问者调用具体的数据
以及在访问者模式中增加模板方法模式等等。​