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

来源:互联网 发布:最牛的请假条淘宝 编辑:程序博客网 时间:2024/06/05 18:08

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

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

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

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

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

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

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

话不多说,我们上代码。

首先是抽象的学校类

    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
原创粉丝点击