游戏中的单例模式

“确保一个类只有一个实例,并为其提供一个全局访问入口。”

提供一个全局指针以访问唯一实例

下面是无法保证线程安全的

class FileSystem 
{ 
public:
 static FileSystem& instance()
 {
   //Lazy initialize.


  if (instance_ == NULL) 
  {
    instance_ = new FileSystem();
  }
  return *instance_;
 }

private:
 FileSystem() {}
 static FileSystem* instance_;
};

更现代的版本,可以保证线程安全

class FileSystem
{
public:
 static FileSystem& instance()
 {
  static FileSystem *instance = new FileSystem();
  return *instance;
 }

private:
 FileSystem() {}
};
class FileSystem
{
public:
 virtual ~FileSystem() {}
 virtual char* read(char* path) = 0;
 virtual void write(char* path, char* text) = 0;
 };

为不同的平台定义派生类

class PS3FileSystem : public FileSystem
{
public:
 virtual char* read(char* path)
 { 
  // Use Sony file IO API...
 }

 virtual void write(char* path, char* text)
 {
  // Use sony file IO API...
 }
};

class WiiFileSystem : public FileSystem
{
public:
 virtual char* read(char* path)
 { 
  // Use Nintendo file IO API...
 }

 virtual void write(char* path, char* text)
 {
  // Use Nintendo file IO API...
 }
};

将FileSystem变为一个单例

class FileSystem
{
public: 
 static FileSystem& instance();

 virtual ~FileSystem() {}
 virtual char* read(char* path) = 0;
 virtual void write(char* path, char* text) = 0;

protected: 
 FileSystem() {}
};

// 不同的平台构造不同的单例对象
FileSystem& FileSystem::instance()
{
#if PLATFORM == PLAYSTATION3
 static FileSystem *instance = new PS3FileSystem();
#elif PLATFORM == WII
 static FileSystem *instance = new WiiFileSystem();
#endif

 return *instance;
}

后悔使用单例的原因

大部分的游戏都不依赖延迟初始化,像这样实现单例模式:

class FileSystem
{
public:
 static FileSystem& instance() { return instance_; }

private:
 FileSystem() {}

 static FileSystem instance_;
};

比如日志对象,我们设计成了单例模式,慢慢的后面发现可能需要多个日志,后面成了火葬场。

将类限制为单一实例

当出现错误使用方式时,我们能够及时法线。

class FileSystem
{
public:
 FileSystem()
 {
  assert(!instantiated_);
  instantiated_ = true;
 }
 ~FileSystem() { instantiated_ = false; }

private:
 static bool instantiated_;
};

bool FileSystem::instantiated_ = false;

优先考虑的方式

class GameObject
{
protected:
 Log& Log() { return log_; }

private:
 static Log& log_;
};

class Enemy : public GameObject
{
 void doSomething()
 {
  getLog().write("I can log!");
 }
};
class Game
{
public:
 static Game& instance() { return instance_; }

 Log&     log()     { return *log_; }
 FileSystem& fileSystem() { return *files_; }
 AudioPlayer& audioPlayer() { return *audio_; }

 // Functions to set log_, et. al. ...



private:
 static Game instance_;
 Log     *log_;
 FileSystem *files_;
 AudioPlayer *audio_;
};

这样起码比一大堆单例要好得多。

Game::instance().getAudioPlayer().play(LOUD_BANG);