1、简介
访问者模式是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。例如,你在朋友家做客,你是访问者,朋友接收你的访问,你通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
PS: 我个人的理解就是,不同的人使用不同的方式,干不同的事,但是最基础的类不会发生变化。
访问者模式(Visitor),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。UML结构图如下:
- Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
- ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
- Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
- ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
- ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
算了,其实刚开始我也看不懂这些定义,就到处翻博客,最后自己写了代码,大致能明白其意思。
2、具体实现
由于代码写的特别简陋,为了防止以后看不懂,这里说明一下,我是按照自己对访问者模式的理解,就是 对于某一个类来说,不同的访问者会干不同的事情。这里我建两个类,分别是自己家(SelfHome)和别人家(OtherHome),两个访问者,自己Self(鸣人)和别人Other(我爱罗)。
interface Home{
//接受访问
public void accept(View view);
}
interface View{
//访问自己家
public void view(SelfHome home);
//访问别人家
public void view(OtherHome home);
}
class SelfHome implements Home{
//自己家,定义地址即可
private String addr;
public SelfHome(String addr){
super();
this.addr = addr;
}
@Override
public void accept(View view) {
view.view(this);
}
public String getAddr(){
return addr;
}
}
class OtherHome implements Home{
//别人家,定义名字和地址
private String name;
private String addr;
public OtherHome(String name, String addr){
super();
this.addr = addr;
this.name = name;
}
@Override
public void accept(View view) {
view.view(this);
}
public String getName(){
return name;
}
public String getAddr(){
return addr;
}
}
class Self implements View{
@Override
public void view(SelfHome home) {
System.out.println("访问自己家,需要知道回家的路线:"+home.getAddr());
}
@Override
public void view(OtherHome home) {
System.out.println("访问别人家,需要知道别人的名字:"+home.getName()+",还有别人家的路线:"+home.getAddr());
}
}
class Other implements View{
@Override
public void view(SelfHome home) {
System.out.println("别人来访问我家,我需要告诉他我在哪住:"+home.getAddr());
}
@Override
public void view(OtherHome home) {
System.out.println("别人回自己家,需要知道回家的路线:"+home.getAddr());
}
}
//访问类,维护访问者和目的地数据
class Visitor{
private List<Home> list = new ArrayList<>();
//添加目的地
public void addHome(Home home){
list.add(home);
}
//访问者访问目的地
public void visit(View view){
for(Home home : list){
home.accept(view);
}
}
}
public class Visit {
public static void main(String[] args) {
Visitor visitor = new Visitor();
//添加目的地
visitor.addHome(new SelfHome("木叶村"));
visitor.addHome(new OtherHome("我爱罗","砂隐村"));
//新建访问者self类
View naLoTo = new Self();
//新建访问者other类
View waAiLo = new Other();
//鸣人的角度
visitor.visit(naLoTo);
//我爱罗的角度,访问木叶
visitor.visit(waAiLo);
}
}
鸣人和我爱罗作为两个访问者,互相访问,代码有点不符合实际,不过也大致能表现访问者模式的主要使用方式。
访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现View接口,然后就可以直接调用Visitor的visit方法去访问账本了。
如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。
3、总结
使用场景:
(1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
(2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
优点:
1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
2、添加新的操作或者说访问者会非常容易。
3、将对各个元素的一组操作集中在一个访问者类当中。
4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。
5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。
缺点:
1、增加新的元素会非常困难。
2、实现起来比较复杂,会增加系统的复杂性。
3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。
适用性:
1、数据结构稳定,作用于数据结构的操作经常变化的时候。
2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。
3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。
ps: 有部分内容参造博客:https://blog.csdn.net/u012124438/article/details/70537203,深受启发,大家可以参考。