设计模式(二十四) 访问者模式

访问者模式提供了一种方法,将算法和数据结构分离。假设我们需要对一个数据结构进行不同的操作,就可以考虑使用访问者模式。访问者模式的要点在于,需要一个访问者接口,提供了一些重载方法来访问具体对象。对于每个具体对象,又提供了一个accept方法来回调访问者。

首先来看看访问者。

public interface Visitor {
    void visit(House house);

    void visit(Kitchen kitchen);

    void visit(LivingRoom livingRoom);

    void visit(BedRoom bedRoom);
}

class HouseVisitor implements Visitor {
    public void visit(House house) {
        System.out.println("访问了房子");
    }

    public void visit(BedRoom bedRoom) {
        System.out.println("访问了卧室");
    }

    public void visit(LivingRoom livingRoom) {
        System.out.println("访问了客厅");
    }

    public void visit(Kitchen kitchen) {
        System.out.println("访问了厨房");
    }
}

然后是要访问的对象,这里是一间屋子。

public class House {
    private LivingRoom livingRoom;
    private Kitchen kitchen;
    private BedRoom bedRoom;

    public House() {
        livingRoom = new LivingRoom();
        kitchen = new Kitchen();
        bedRoom = new BedRoom();
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
        livingRoom.accept(visitor);
        bedRoom.accept(visitor);
        kitchen.accept(visitor);
    }
}

class LivingRoom {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Kitchen {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class BedRoom {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

然后客户端就可以简单的访问屋子了。

    public void run() {
        Visitor visitor = new HouseVisitor();
        House house = new House();
        visitor.visit(house);
    }

这就是访问者模式了。可能会同学会有疑问,为什么我要这么写?如果让屋子对象层次全部都实现Visitor接口,然后客户端直接调用这些visit方法不是也可以吗?一开始我也有这个疑问,后来看了知乎轮子哥的一篇文章ParserGen生成预定义好的各种visitor,感觉茅塞顿开。

其实Visitor模式讲的就是在不需要扩充新的子类的时候,如何添加新的虚函数而不需要修改原有代码。当然虚函数也有它的好处,就是添加新的子类的时候不需要修改原有代码。所以看你的业务逻辑,到底是添加新子类多,还是添加新虚函数多,从而选择要不要把程序写成基于Visitor模式的样子。 对于编译器来说,整个处理流程那么复杂,所以等于需要经常添加虚函数,因此就都把本来是虚函数的东西改成了各种Visitor。这个时候,如果你修改了语法,那么每一个Visitor都会曝出语法错误,所以这等于变相通知你所有需要修改的东西在哪里——如果你能坚持不因为偷懒而使用dynamic_cast的话。

所谓设计模式,都需要在特定的环境中才有用。所以现在我们已经了解了什么情况下应该使用访问者模式。假如接口中的方法固定,但是需要添加新的实现类,那么就使用普通的继承方式。如果接口方法经常变动,就可以把接口改写为访问者。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GopherCoder

『Go 语言学习专栏』-- 第九期

13610
来自专栏精讲JAVA

用 Maven 实现一个 protobuf 的 Java 例子

Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化,常用于RPC 系统(Remote Procedure...

15920
来自专栏calmound

缺省参数是编译期间绑定的,而不是动态绑定

看一个程序 #include <iostream> using namespace std; class A { public: virtual void ...

32760
来自专栏进击的君君的前端之路

Vue成神之路之选项

vue.js——开发版本:包含完整的警告和调试模式 vue.min.js——生产版本:删除了警告,进行了压缩

13340
来自专栏xingoo, 一个梦想做发明家的程序员

Java程序员的日常 —— 工作一天的收获

看题目可能是扯皮,其实还是有很多专业知识的。从最开始没有注意到设计原则,到后面的jquery实战技巧,都是今天一天碰到的问题。 每天整理一点点,每天收获一点...

31570
来自专栏游戏开发那些事

【Cocos2d-x游戏开发】Cocos2d-x中的数据存储技术

  数据存储和网络功能可以说是一款游戏中必不可少的功能,如果一款游戏不能保存进度那么它的可玩性必然大打折扣(试想一下,玩家辛辛苦苦玩了一整天的游戏,结果退出时告...

10010
来自专栏有趣的django

python开发面试问题

python语法以及其他基础部分 可变与不可变类型;  浅拷贝与深拷贝的实现方式、区别;deepcopy如果你来设计,如何实现;  __new__() 与 __...

49480
来自专栏一“技”之长

iOS中CoreData数据管理系列一——初识CoreData

    CoreData是一个专门用来管理数据的框架,其在性能与书写方便上都有很大的优势,在数据库管理方面,apple强烈推荐开发者使用CoreData框架,在...

6630
来自专栏逍遥剑客的游戏开发

Ogitor代码分析

14020
来自专栏铭毅天下

干货 | Elasticsearch Nested类型深入详解

本文通过一个例子将Nested类型适合解决的问题、应用场景、使用方法串起来, 文中所有的DSL都在Elasticsearch6.X+验证通过。

33630

扫码关注云+社区

领取腾讯云代金券