服务定位器

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

例如

// Use a static class?

AudioSystem::playSound(VERY_LOUD_BANG);

// Or maybe a singleton?

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

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

服务定位器模式

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

服务要暴露的接口

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)
 {
  // 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...

 }
};

简单的定位器

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

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

private:
 static Audio* service_;
};

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

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

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

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

空服务

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

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)
 {
  // Revert to null service.


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

  service_ = service;
 }

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

日志装饰器

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

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

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

 // Swap it in.

 Locator::provide(service);
}

服务是如何被定位的

通过外都代码 进行 provide

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

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

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

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

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

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

服务的作用域多大

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

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

private:
 static Audio* service_;
};