理论要点

  • 什么是服务定位器模式:提供服务的全局接入点,而不必让用户和实现它的具体类耦合。通俗点讲就是服务类定义了一堆操作的抽象接口,具体的服务提供者实现这些接口。分离的定位器来管理服务类,外部就是通过这个定位器对象来间接获取服务

  • 要点
    1,一般通过使用单例或者静态类来实现服务定位模式,提供服务的全局接入点。和单例模式很像,只是多了一个间接获取服务对象的中间管理类。不让用户直接接触具体服务类。

    2,服务定位模式可以看做是更加灵活,更加可配置的单例模式。如果用得好,它能以很小的运行时开销,换取很大的灵活性。相反,如果用得不好,它会带来单例模式的所有缺点以及更多的运行时开销。

    3,使用服务定位器的核心难点是它将依赖,也就是两块代码之间的一点耦合,推迟到运行时再连接。这有了更大的灵活度,但是代价是更难在阅读代码时理解其依赖的是什么。

  • 使用场合
    1,服务定位模式在很多方面是单例模式的亲兄弟,在应用前应该考虑看看哪个更适合你的需求。

    2,让大量内容在程序的各处都能被访问时,就是在制造混乱。对何时使用服务定位模式的最简单的建议就是:尽量少用。

    3,与其使用全局机制让某些代码直接接触到它,不妨先考虑将对象传过来。因为这样可以明显地保持解耦,而且可以满足我们大部分的需求。当然,有时候不方便手动传入对象,也可以使用单例的方式。

    4,Unity引擎在它的GetComponent()方法中使用了这个模式,协助组件模式的使用,方便随时获取到指定的组件。还有微软的XNA框架将这个模式内嵌到它的核心类Game中。每个实例有一个 GameServices 对象,能够用来注册和定位任何类型的服务。

代码分析

1,首先我们来看看这样一个游戏情节,还是以音频系统为例。我们游戏中很多系统都要访问它。 滚石撞击地面(物理)。 NPC狙击手开了一枪,射出子弹(AI)。 用户选择菜单项需要响一声确认(用户界面)。。。每处都需要像下面这样调用音频系统:

// 使用静态类?
AudioSystem::playSound(VERY_LOUD_BANG);// 还是使用单例?
AudioSystem::instance()->playSound(VERY_LOUD_BANG);

尽管这样可以获得我们想要的结果,但是这里有些微妙的耦合。每个调用音频系统的游戏部分直接引用了具体的AudioSystem类。这里的音频系统我们可以看做是服务提供者,而游戏很多模块使用处则是服务使用者,想想其中的问题:首先明显的是隐私问题,服务提供者直接暴露给了很多使用者。然后是耦合问题,一旦服务提供者发生修改,那么所有的使用者都需要对应修改。

2,既然问题已经知道了,那么解决起来就很容易了,无非是想办法让服务提供者类和使用者解耦。那么只需要有个中间对象来连接它们两者就可以了,这个中间对象就是我们这节讲的服务定位器。
我们以音频服务为例,首先来看看这个服务提供者怎么实现,即我们的音频系统:
下面是服务要暴露的接口:

class Audio
{
public:virtual ~Audio() {}virtual void playSound(int soundID) = 0;virtual void stopSound(int soundID) = 0;virtual void stopAllSounds() = 0;
};

然后是服务具体提供者,继承实现这些接口:

class ConsoleAudio : public Audio
{
public:virtual void playSound(int soundID){// 使用主机音频API播放声音……}virtual void stopSound(int soundID){// 使用主机音频API停止声音……}virtual void stopAllSounds(){// 使用主机音频API停止所有声音……}
};

好,现在服务提供者已经实现了,接下来就是我们的服务定位器了。

class Locator
{
public:static Audio* getAudio() { return service_; }static void provide(Audio* service){service_ = service;}private:static Audio* service_;
};

服务对象由外部传入,使用关系就从这样了:服务类<—>定位器<—>使用者。
外部使用:

Audio *audio = Locator::getAudio();
audio->playSound(VERY_LOUD_BANG);

定位器初始化:

ConsoleAudio *audio = new ConsoleAudio();
Locator::provide(audio);

嗯,这个简单的服务定位器就已经粗略实现了。不过还有个明显的漏洞,看看如果定位器没有初始化我们就使用了它,那就直接崩溃了。
下面我们来做个安全操作,实现一个空服务:

class NullAudio: public Audio
{
public:virtual void playSound(int soundID) { /* 什么也不做 */ }virtual void stopSound(int soundID) { /* 什么也不做 */ }virtual void stopAllSounds()        { /* 什么也不做 */ }
};

现在,我们将服务定位器修改下:

class Locator
{
public:static void initialize() { service_ = &nullService_; }static Audio& getAudio() { return *service_; }static void provide(Audio* service){if (service == NULL){// 退回空服务service_ = &nullService_;}else{service_ = service;}}private:static Audio* service_;static NullAudio nullService_;
};

调用代码永远不知道“真正的”服务没找到,也不必担心处理NULL。 这保证了它永远能获得有效的对象。

其实避免空对象还有一个思路,就是跳过运行时定位器初始化过程,让它在编译时就初始化,就像这样:

class Locator
{
public:static Audio& getAudio() { return service_; }private:#if DEBUGstatic DebugAudio service_;#elsestatic ReleaseAudio service_;#endif
};

它快速,也能保证服务一直可用,但是它没法改变服务了。一般这种方式我们使用的少,还是运行时初始化灵活性更大,至于避免空指针报错,上面说了创建一个什么也不做的空对象,然而这样对于查错不方便,有时我们可以直接使用断言,让游戏停止,暴露错误:

class Locator
{public:static Audio& getAudio(){Audio* service = NULL;// Code here to locate service...assert(service != NULL);return *service;}
};

嗯,服务定位器模式就先介绍到这了,和我们的单例模式很类似,核心就是多了个间接层:定位器!

解耦模式--服务定位器相关推荐

  1. Java服务定位器模式

    服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候.考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术.在首次请 ...

  2. J2EE业务层模式:服务门面,应用服务,以及业务委托,服务定位器

    现在J2EE领域无论是表现层,业务层还是持久层,框架满天飞,虽然说框架为我们省了很大的力气,但是我们还是需要掌握J2EE里面经常用到的一些模式,下面对J2EE领域业务层的几个模式做一个小的总结: 服务 ...

  3. 避免在ASP.NET Core中使用服务定位器模式

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:服务定位器(Service Locator)作为一种反模式,一般情况下应该避免使用,在 ...

  4. 三十六、服务定位器模式 (Service Locator Pattern)

    服务定位器模式(Service Locator Pattern)用于想使用 JNDI 查询定位各种服务的时候 考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术 在首次请求某 ...

  5. php service locator,服务定位器模式(Service Locator)

    服务定位器模式(Service Locator) 服务定位器模式被一些人认为是一种反面模式.它违反了依赖倒置原则.该模式隐藏类的依赖,而不是暴露依赖(如果暴露可通过依赖注入的方式注入依赖).当某项服务 ...

  6. Service Locator服务定位器

    先讲服务定位器, 有些摘录于 http://www.digpage.com/convention.html Service Locator目的也在于解耦他的模式非常贴合Web这种基于服务和组件的应用的 ...

  7. php service locator,Yii源码解读-服务定位器(ServiceLocator)

    SL的目的也是解耦,并且非常适合基于服务和组件的应用. Service Locator充当了一个运行时的链接器的角色,可以在运行时动态地修改一个类所要选用的服务, 而不必对类作任何的修改. 一个类可以 ...

  8. 基于ACE的服务定位器(图失效)

    军械工程学院本科毕业论文 基于ACE的服务定位器 2006届计算机工程专业 指导教员:张剑炜 学员姓名:庄泽南 军械工程学院 二零零六年六月 论 文 摘 要 ACE自适配通信环境(ADAPTIVE C ...

  9. 服务定位器 - Caliburn.Micro 文档系列

    文章目录 服务定位器 (Service Locator) 入门 为 IoC 注入功能 在你的应用中使用 IOC 获取单一服务 获得一系列服务 注入实例 服务定位器 (Service Locator) ...

  10. php 服务定位,服务定位器(Service Locator)

    服务定位器 服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象.在服务定位器中,每个组件都只有一个单独的实例,并通过ID 唯一地标识.用这个 ID 就能从服务定位器中得到这个组件. 在 Y ...

最新文章

  1. 利用rawcap抓包(自己发给自己的包裹)
  2. mysql把一行保存到另一个表_MYSQL:如何复制整个行从一个表到另一个在MySQL与第二个表有一个额外的列?...
  3. sicktim571操作手册_SICK激光传感器TIM310操作说明书
  4. Mybatis 常见知识点问题
  5. number 限制最长数字_阿博的Python之路Number数据类型详解
  6. SpringCloud(一)
  7. 访问win10的远程桌面(Remote Desktop)总是凭据或者用户密码错误
  8. 190717每日一句
  9. 【TSP】基于matlab改进的人工鱼群算法求解旅行商问题【含Matlab源码 1479期】
  10. TiDB DevCon2018.tick(1.20)
  11. python实现银行ATM系统
  12. 本特利探头177230-01-02-CN
  13. 完成一个个人博客,博客头像可上传本地图片;部分图片实现点击看大图功能
  14. 刑事实务办案中疑难问题
  15. 手机计算机文件夹加密文件,手机文件夹加密锁软件加密步骤【图文教程】
  16. make makefile cmake qmake都是什么,有什么区别
  17. DataInputStream.readUTF返回返回EOFException
  18. 1413. 逐步求和得到正数的最小值
  19. python namedtuple用法_详解Python中namedtuple的使用
  20. 最新计算机专业毕业设计论文选题源码演示录像下载(开题报告任务书PPT毕业答辩模板jsp1887 ATM机银行模拟实现springmvc

热门文章

  1. 单片机c语言视频教程bd,单片机C语言教程五
  2. delphi 2010之如何快速开发原生ActiveX控件
  3. matlab序列码,Matlab7 R14(sn 序列号)
  4. 计算机VFP试题答案,2010全国计算机vfp二级笔试试题及答案
  5. Ubuntu 下安装 GCC 的方法
  6. NLPIR系统的中文语义分析模式介绍
  7. python语言程序设计实践教程实验八答案_Python程序设计实践教程
  8. 遗传算法解决作业调度c语言,应用遗传算法解决车间作业调度问题
  9. svn中文语言安装包使用
  10. 阿里云开发-身份证图像识别(SpringBoot)