先从例子开始:
需求:现有Item类接口IItem和其具体类Item_A,Item_B,Item_C。若我们想给Item添加功能,则可以这样:(不是访问者模式)
每个Visitor都是一个新的功能。这样要拓展Item只需要实现派生类,拓展功能也是只需要实现Visitor的派生类。
但现在需求发生改变,对于每一个具体的Visitor来说,Item不同,执行的代码也不同。也就是说要针对每个具体的Item,都要有一个函数与之对应:
也就是这样,visitor不能再针对Item的接口编程了,要针对具体编程。当然,针对具体编程,接口也就没有必要了。
这样就能对Item_A和Item_B添加功能了,但缺点是它违背了开闭原则,当我们想要拓展Item时,整个Visitor都要重写。但当我们想要拓展功能(Visitor)时,就很简单了,执行要继承Visitor就行了。
接下来需求再次发生改变,要将多个Item放到一个大容器里,然后对这些Item执行某个功能,且这个功能可随时拓展。
现在只需要调用Container的accept函数,并传入一个visitor,就能依次让所有Item调用accept函数了,Item的accept为虚函数,它再调用v.doSomething来选择具体调用visitor 的哪个重载函数。每个Item都要重写accept函数。虽然每次重写的代码都相同,但仍不能省略。因为只有重写了,this的静态类型才会发生改变,这样才能根据类型选择不同的重载函数。
这就是一个访问者模式的最终效果图。
它应用场合是:元素Item不会拓展,但功能Visitor会拓展。
优点:可方便的拓展Visitor
缺点:不能拓展Item。一旦拓展,就会导致这个Visitor系统重写。
cocos2dx3.0中就用到了这种模式。
cocos2dx只提供了一个visitor,是用于打印的。如果有需要,可以通过继承DataVisitor来实现。