设计模式学习之访问者模式

访问者模式在设计模式中应该算是比较复杂的了,但也不能成为我们不学习的理由。

定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

听起来就很绕,首先看数据结构这四个关键字,这就是不变的部分。比如说世界上只有男人和女人,当然(其他的忽略吧),这个就是很稳定的。如果是数据结构频繁变化的是不太适合用这个模式的。

那么变化的部分呢?
数据结构各个原色的操作,也就说变的是操作。
下面我举个例子,这也是我代码里面的例子。

我们现在的教育体系,从我们上学开始一直到大学结束,还是相对来说比较稳定的,为了方便写(其实是偷懒),我假设只有三种学校,而不是我们从幼儿园到初中,高中,大学的节奏。

而每一个阶段,都有要学习的相同科目。
比如说语文,从小学到大学可是都有的吧。
那么这个语文就是所说的操作
当然,在这个例子里面其实有一个问题,学科其实也是很稳定的,但是我们假设学科是不稳定的,我现在只想到这个适应场景。

最开始,只有一个语文学科,但是每个阶段学习的语文都是不一样的,小学你可能只是认识字,初中高中你应该会造句写作文了阅读文章,大学可能学的是更加偏应用的应用文。

话不多说,我们上代码。

首先是抽象的学校类

    public abstract class School {
    public String name;

    public String getName() {
        return name;
    }

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

    public abstract void acceptTeach(Subject subject);

}

接下来就是我们继承学校类的

小学类

   public class PrimarySchool  extends School {
     public PrimarySchool() {
         this.name="xiao xue";
     }

    @Override
    public void acceptTeach(Subject subject) {
        subject.primaryTeach(this);
    }

}

中学类

public class MiddleSchool extends School{
    public MiddleSchool() {
         this.name="zhong xue";
     }
    @Override
    public void acceptTeach(Subject subject) {
        subject.middleTeach(this);
    }

}

大学类

public class HighSchool extends School {
    public HighSchool() {
         this.name="da xue";
     }
    @Override
    public void acceptTeach(Subject subject) {
        subject.HighTeach(this);
    }

}

我们发现累在抽象的父类里面有一个抽象的方法acceptTeach,接受一个学科
这里我把这个写成了接口,至于具体是接口还是抽象类各位根据实际情况判断。

所以学科类来了

public interface Subject {

    void primaryTeach(School school);
    void middleTeach(School school);
    void HighTeach(School school);
}

现在我们来写实现类

public class Chinese implements Subject{

    @Override
    public void primaryTeach(School school) {
        System.out.println(school.name+":一 二 三");
    }

    @Override
    public void middleTeach(School school) {
        System.out.println(school.name+":谁知盘中餐");
    }

    @Override
    public void HighTeach(School school) {
        System.out.println(school.name+":杨柳岸晓风残月");
    }

}

现在我们写一个测试类

public class TestVisitor {

    @Test
    public void test() {
        PrimarySchool primarySchool=new PrimarySchool();
        MiddleSchool middleSchool=new MiddleSchool();
        HighSchool highSchool=new HighSchool();
        Subject subject=new Chinese();

        primarySchool.acceptTeach(subject);
        middleSchool.acceptTeach(subject);
        highSchool.acceptTeach(subject);
    }

}

我们现在看到,结果是什么呢?

xiao xue:一 二 三
zhong xue:谁知盘中餐
da xue:杨柳岸晓风残月

看到这里,大家有没有发现什么呢?

我们应该知道有一个设计原则

类应该对修改关闭,而对扩展开放。

我们现在想想,如果我们现在的学校需要增加了新的课程,比如说 数学。

那么我们需要做什么呢?

我们现在只需要增加一个实现学科类的数学类,并且实现接口里面的方法就好了

public class Math implements Subject{

    @Override
    public void primaryTeach(School school) {
        System.out.println(school.name+":1+1");
    }

    @Override
    public void middleTeach(School school) {
        System.out.println(school.name+":log(2)");
    }

    @Override
    public void HighTeach(School school) {
        System.out.println(school.name+":csc x (sec x)");
    }

}

而其他的类呢?

什么都不用改变!

看看我们的测试结果

public class TestVisitor {

    @Test
    public void test() {
        PrimarySchool primarySchool=new PrimarySchool();
        MiddleSchool middleSchool=new MiddleSchool();
        HighSchool highSchool=new HighSchool();
        //Subject subject=new Chinese();
        Subject subject=new Math();

        primarySchool.acceptTeach(subject);
        middleSchool.acceptTeach(subject);
        highSchool.acceptTeach(subject);
    }

}

xiao xue:1+1
zhong xue:log(2)
da xue:csc x (sec x)

说了这么多,我们学习所谓访问者那么在这里面谁是访问者呢?

class A {  
    public void method1(){  
        System.out.println("我是A");  
    }  

    public void method2(B b){  
        b.showA(this);  
    }  
}  

class B {  
    public void showA(A a){  
        a.method1();  
    }  
} 

我们主要来看一下在类A中,方法method1和方法method2的区别在哪里,方法method1很简单,就是打印出一句“我是A”;方法method2稍微复杂一点,使用类B作为参数,并调用类B的showA方法。再来看一下类B的showA方法,showA方法使用类A作为参数,然后调用类A的method1方法,可以看到,method2方法绕来绕去,无非就是调用了一下自己的method1方法而已,它的运行结果应该也是“我是A

所以上面的例子我们也就看出来了 谁是访问者呢?
那么就是所有的操作类 也就是访问者,语文会访问所有的元素,所有的学校类
数学也会访问所有的元素累

而其实一般来说,操作累应该还有一个遍历的方法
就是把所有的元素都放在一个集合或者容器里面

访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。

我的例子只是用到了一个,没有这么写

其实这是一种双重抽象,

访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。

相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的操作无须修改原有系统,系统具有较好的可扩展性

访问者模式并不是那么完美,它也有着致命的缺陷:增加新的元素类比较困难。通过访问者模式的代码可以看到,在访问者类中,每一个元素类都有它对应的处理方法,也就是说,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,一个项目的基本功能已经确定下来,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值