# 服务定位器

“为某服务提供一个全局访问入口来避免使用者与该服务具体实现类之间产生耦合。”

例如

```cpp
// Use a static class?

AudioSystem::playSound(VERY_LOUD_BANG);

// Or maybe a singleton?

AudioSystem::instance()->playSound(VERY_LOUD_BANG);
```

该怎么选，有没有更好的方式。

## 服务定位器模式

一个服务类为一系列操作定义了一个抽象的接口。一个具体的服务提供器实现这个接口。一个单独的服务定位器通过查找一个合适的提供器来提供这个服务的访问，它同时屏蔽了提供器的具体类型和定位这个服务的过程。

### 服务要暴露的接口

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

### 服务提供器

```cpp
class ConsoleAudio : public Audio
{
public:
　virtual void playSound(int soundID)
　{
　　// Play sound using console audio api...

　}

　virtual void stopSound(int soundID)
　{
　　// Stop sound using console audio api...

　}

　virtual void stopAllSounds()
　{
　　// Stop all sounds using console audio api...

　}
};
```

### 简单的定位器

```cpp
class Locator
{
public:
　static Audio* getAudio() { return service_; }

　static void provide(Audio* service)
　{
　　service_ = service;
　}

private:
　static Audio* service_;
};
```

静态函数 getAudio() 负责定位工作，我们能在代码的任何地方调用它，它能返回一个 Audio 服务的实例供我们使用。

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

在使用这个服务之前，它依赖一些外部代码来注册一个服务提供器,当游戏启动时，它调用类似

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

## 空服务

为了防止空指针崩溃，可以定义一个“null”服务提供器

```cpp
class NullAudio: public Audio
{
public:
　virtual void playSound(int soundID) 
　virtual void stopSound(int soundID) 
　virtual void stopAllSounds()　　　　
};
```

仅实现服务接口，但实际上什么也不做

```cpp
class Locator
{
public:
　static void initialize()
　{
　　service_ = &nullService_;
　}

　static Audio& getAudio() { return *service_; }

　static void provide(Audio* service)
　{
　　// Revert to null service.


　　if (service == NULL) service = &nullService_;

　　service_ = service;
　}

private:
　static Audio* service_;
　static NullAudio nullService_;
};
```

## 日志装饰器

```cpp
class LoggedAudio : public Audio
{
public:
　LoggedAudio(Audio &wrapped) : wrapped_(wrapped) {}

　virtual void playSound(int soundID)
　{
　　log("play sound");
　　wrapped_.playSound(soundID);
　}

　virtual void stopSound(int soundID)
　{
　　log("stop sound");
　　wrapped_.stopSound(soundID);
　}

　virtual void stopAllSounds()
　{
　　log("stop all sounds");
　　wrapped_.stopAllSounds();
　}

private:
　void log(const char* message)
　{
　　// Code to log message...

　}

　Audio &wrapped_;
};
```

可以兼容的传给 Locator

```cpp
void enableAudioLogging()
{
　// Decorate the existing service.

　Audio *service = new LoggedAudio(
　　 Locator::getAudio());

　// Swap it in.

　Locator::provide(service);
}
```

## 服务是如何被定位的

* 外部代码注册

通过外都代码 进行 provide

* 编译时绑定

```cpp
class Locator
{
public:
　static Audio&getAudio() { return service_; }

private:
　#if DEBUG
　　static　DebugAudio service_;
　#else
　　static　ReleaseAudio service_;
　#endif
};
```

* 在运行时配置

走配置，不需要编译就能切换服务提供器

## 当服务不能被定位时发生什么

* 让使用者处理，调用者需要判断处理NULL
* 终止游戏,例如使用断言

```cpp
class Locator 
{ 
public:　 
　static Audio& getAudio()　 
　{
　　Audio* service = NULL;　　　
　　// Code here to locate service...　　　

　　assert(service != NULL);　　 
　　return *service;　 
　} 
};
```

* 返回一个空服务，前面我们有介绍

## 服务的作用域多大

* 全局访问
* 访问被限制到类中，比如

```cpp
class Base
{
　// Methods to locate service and set service_...

protected:
　// Derived classes can use service
　static Audio& getAudio() { return *service_; }

private:
　static Audio* service_;
};
```
