设计模式-访问者模式(Visitor pattern)理解

马上期末了,一直没看懂访问者模式,今天查了好多资料,算是搞明白了。在这分享一下自己的理解。

访问者模式,顾名思义就是遍历集合时,用来访问集合中元素的。这个集合中元素的类型不同,但类型都是已知的,且未来不会改变。访问单个元素的方式有多种,每种方式在访问不同类型的元素时所做的操作不同,并且未来可能会有新的访问方式。


举例来说:

一个班级里有学霸和学渣两种类型的学生。当出成绩时,学霸开心,学渣不开心。当放假时,学霸不开心,学渣开心。未来不会有第三种类型的学生,但未来可能会有新的事件发生,比如布置作业、春游等。

现在要求写代码实现打印放假时和出成绩时学生们的反应,代码怎么写呢?最简单最容易想到的方法,用if语句和instanceof判断类型,执行不同操作,伪代码如下:

abstract class Student{}    // 抽象学生类
class XueBa extends Student{}    // 学霸
class XueZha extends Student{}    // 学渣

main{
    List<Student> students = {new XueBa(), new XueZha(), ....}; // 班级学生,只有学霸和学渣两种类型
    
    // 出成绩了
    for(Student s: students){
        if(s instanceof XueBa){    // 学霸开心
            print("出成绩了,开心");
        }else if(s instanceof XueZha){    //学渣不开心
            print("出成绩了,不开心");
        }
    }
    
    // 放假了
    for(Student s: students){
        if(s instanceof XueBa){    //学霸不开心
            print("放假了不能学习,不开心");
        }else if(s instanceof XueZha){    //学渣开心
            print("放假了,开心");
        }
    }
    
}

但这样写明显不符合面向对象的开放封闭原则,怎么改呢?有一个容易想到的方法,就是把出成绩和放假作为一个抽象方法,写到抽象学生类里面,学霸和学渣分别实现这两个方法(这种做法也不对,后面会说原因):

abstract class Student{    // 抽象学生类
    public void vacation();    // 放假方法
    public void releaseScore();    // 出成绩方法
}
class XueBa extends Student{    //学霸实现两个方法
    public void vacation(){
        print("放假了不能学习,不开心");
    }
    public void releaseScore(){
        print("出成绩了,开心");
    }
}
class XueZha extends Student{    // 学渣实现两个方法
    public void vacation(){
        print("放假了,开心");
    }
    public void releaseScore(){
        print("出成绩了,不开心");
    }
}

main{
    List<Student> students = {new XueBa(), new XueZha(), ....}; // 班级学生,只有学霸和学渣两种类型
    
    // 出成绩了
    for(Student s: students){
        s.releaseScore();
    }
    
    // 放假了
    for(Student s: students){
        s.vacation();
    }
}

这样写,看上去很符合开放封闭原则,因为如果有新的类型的学生,只需要添加一个新的学生类,让它去实现两个抽象方法即可,不用改其它类的代码。

但仔细看题目,题目说不会有新的类型学生,而是会有新类型的事件!

也就是说,如果现在要春游,就需要在抽象学生类里加一个springOuting()方法,然后分别在学霸和学渣里实现。一共需要改三个类!一点也不符合开放封闭原则。

所以一定要搞清楚固定的东西是什么,可能会变的东西是什么。

因为会变的是遍历列表时对学生的访问方法(出成绩、放假、春游),而不是学生类型(学霸、学渣),所以这里就不应该像上面那样将学生的行为抽象,而是应该将访问的方式抽象:

abstract class Student{    // 抽象学生类
    public void accept(Visitor v);    // 处理Visitor这个抽象事件。(可能是出成绩、放假等。这里抽象化了,以便随时改)
}
class XueBa extends Student{    // 学霸处理事件
    public void accept(Visitor v){
        v.visitXueBa(this);    // 调用对学霸的处理方法
    }
}
class XueZha extends Student{    // 学渣处理事件
    public void accept(Visitor v){
        v.visitXueZha(this);    // 调用对学渣的处理方法
    }
}

abstract class Visitor{    // 抽象的访问方式类
    public void visitXueBa(XueBa x);    // 访问学霸
    public void visitXueZha(XueZha x);    // 访问学渣
}
这样写好以后,要添加访问方法(出成绩、放假、春游),只需要继承Visitor类,并实现学霸和学渣分别不同的反应即可。比如出成绩:
class ReleaseScoreVisitor extends Visitor{
    public void visitXueBa(XueBa x){
        print("出成绩了,开心");
    }
    public void visitXueZha(XueZha x){
        print("出成绩了,不开心");
    }
}
主函数:
main(){
    List<Student> students = {new XueBa(), new XueZha(), ....}; // 班级学生,只有学霸和学渣两种类型
    
    Visitor v = new ReleaseScoreVisitor(); // 先确定访问方式,“出成绩”
    
    for(Student s: students){
        s.accept(v);    // 学生s接受v方式的访问
    }
}
现在要添加访问方法(放假、春游等),就只需要 添加访问方式类,而不需要修改上面写好的类,所以这样才真正符合开放封闭原则。
在遍历集合时,把访问单个对象的 方式抽象出来,这就是访问者模式。

所以说,一定要搞清楚固定的东西是什么,可能会变的东西是什么,才好理解访问者模式的思想。



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值