游戏编程模式之单例模式

“确保一个类只有一个实例,并为其提供一个全局访问入口。”(摘自《游戏编程模式》)

  • 单例的设计对象本身就不应该拥有多个实例,或如果拥有就不能正常运行。
  • 适合使用单例模式的例子有:文件封装类(同一个文件的读与写操作不能同时发生)、日志记录、文件加载、游戏状态存储等
  • 单例模式提供了一个单一的实例、提供了全局获取位移实例的方法,这是该模式的重要特性。
  • 单例模式是一个双刃剑,我们使用时要保持清醒的头脑,他或许在短期内具有比较好的收益,但我们也应考虑如何避免使用单例模式
提供全局指针以访问唯一实例
  • 实现代码:
    class FileSystem
    {
        public:
          static FileSystem& instance()//获取唯一实例的全局方法
          {
              if(m_Instance==NULL)
                m_Instance=new FileSystem();
              return m_FileSystem;
          }
          
        private:
          FileSystem()  {}
          
          static FileSystem* m_Instance;
    };
    
  • 更现代的版本
    class FileSystem
    {
        public:
          static FileSystem& instance()
          {
              static FileSystem* instance =new FileSystem();
              return *instance;
          }
        
        private:
          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() {}
    };
    
    //继承来为不同平台定义派生类
    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& FileSystem::instance()
    {
        #if PLATFORM == PLAYSTATION3
          static FileSystem* instance=new PS3FileSystem();
        #elif PLATFRORM == WII
          static FileSystem* instance=new WiiFileSystem();
        #endif
    }
    
单例模式不好的方面
  • 它是一个全局变量,而全局变量是有害的。
    • 全局变量令代码变得生涩。
    • 全局变量促进了耦合
    • 全局变量对并发不友好。
怎么处理单例模式
  • 是否需要类:在设计类时,并不是所有都适合新创建一个类,而是将其包含至同一类便可。过多创建类或许是贪图思考的遍历,这样的代码结构或许太松散,例子如下:
    class Bullet
    {
        public:
          int  getX() const {return X_;}
          int  getY() const {return Y_;}
          void setX(int x) {X_=x;}
          void setX(int y) {Y_=y;}
        private:
          int X_;
          int Y_;
    }
    
    //管理它生成的类-------思考一下是否有必要创建
    class BulletManager
    {
        public:
        Bullet* create(int x,int y)
        {
            Bullet* bullet=new Bullet();
            bullet->setX(x);
            bullet->setY(y);
            return bullet;
        }
        
        bool isOnScreen(Bullet& bullet)
        {
            return bullet->getX()>=0&&
                   bullet->getX()<=WIDTH&&
                   bullet->getY()>=0&&
                   bullet->getX()<=HEIGHT;
        }
        //more Operate
    }
    
    //我们在处理上可以整合成一个类,将上面所需方法完全整合成一个类Bullet
    代码再给出,详细代码请阅读原书。
    
  • 将类限制为单一实例:用于不需要全局访问的情况。当我们只需要再某一段代码访问时,只需要在类中设置一个bool的标志,用来确定是否已经初始化生成过实例。代码如下:
    class FileSystem
    {
        public:
          FileSystem()
          {
              assert(!instantiated_);
              instantiated_=true;
          }
          
          ~FileSystem() { instantiated_=false; }
          
          private bool instantiated_;
    }
    bool FileSystem::instantiated_=false;
    
    • 生成一个实例后,tag变量instantiated_为true,这时不允许实例化,保证仅仅存在该类的一份实例。当析构时,唯一实例被释放,tag变量instantiated_重新回到false,这时类可再次实例化。
  • 为实例提供一个便捷的访问方式
    • 便利的访问是我们使用单例的主要原因,他让我们随时随地获取所需对象。
    • 当使用单例时,通用的原则是:在保证功能的情况下将变量限制在一个狭小的范围内。
总结
  • 单例模式便于我们去访问一个对象
  • 单例模式使用场景为:在全局或某一个区域只应该存在一个实例的时候
  • 单例模式要慎用!!!可以使用其他手段完成单例模式的思想

知识点、图片全部源于《Game Programming Patterns》,中文名《游戏编程模式》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值