此为RobertNystrom著作游戏编程模式的读书笔记。
第16章-服务定位器
在cocos中的音频播放一般使用SimpleAudioEngine::getInstance()->playBackgroundMusic()这个即为文中提及的使用单例访问服务。
而这里文章指出使用服务定位器解耦了一个服务是什么和一个服务在哪里。
首先一个抽象的Audio类规定暴露服务的结构是啥,以便外界调用。几个子类(concreteAudio)实现Audio,一个Locator依赖Audio型对象的指针。使用的时候实际上是通过Locator::getAudio()来获取到Audio型对象(实际上是其实现子类concreteAudio)。在使用Locator::getAudio()之前注意要对Locator注入依赖,即:
concreteAudio* audio=new concreteAudio();
Locator::privide(audio); //依赖注入
其他模块中使用代码
Audio* audio=Locator::getAudio();
audio->playSound(_SOMETHING_);
不过要有一个防止未注入依赖而产生空服务的机制,为此我们定义一个Audio的子类叫NullAudio,用于实现Audio中接口但是什么都不做,如果未注入依赖就默认返回这个NullAudio类型的引用。(为什么是引用而不是指针,因为C++中引用不会为NULL)
此模式的优点一:其作用在于方便关闭服务,比如关闭音效。
优点二:是可以实现对服务的扩展,比如日志装饰器,不同领域的程序员如AI程序员或音效程序员都需要log来调试,直接使用log会照成日志太多。我们可以将不同系统的条件日志作为服务暴露出去,利用装饰器模式实现它。我们可以定义LoggedAudio类,继承自Audio,并保留一个Audio*做为被装饰者wrapped,用wrapped实现Audio的各接口,但是又在各接口中把log这件事干了。随后我们把LoggedAudio为依赖注入给Locator。现在任何的音频服务在调用时均被log出来了。
优点三:可以替换服务,比如某服务可以由网络来完成,而使用Locator的代码不必知道。还可以在编译时绑定,即根据宏定义定义Locator中的不同Audio*,不过最牛逼的还是根据配置文件取出字符串,然后利用反射绑定想要的Audio*服务。unity中GetComponent()方法中就利用了这个模式。