cocos2dx几种设计模式之一

单例设计模式

在cocos2dx中存在不少的单例,虽然单例的设计模式引起不少弊端。我们使用单例目的就是获得全局的唯一一个对象,来做一些事情,那么什么时候用单例什么时候不用单例呢。我觉得一个是从道理上来说,单例在全局应该是唯一的,比如cocos2dx中的导演类,一个游戏应该只有一个导演去完成一些功能,还有就是当你需要在一个类中初始化一个需要设定为单例的对象,为这个对象的成员变量赋值,当在另一类中的时候,我们需要取得这个对象中的成员变量的值的时候,这样就设计成单例的,虽然通过其他的方法也可以完成任务,我觉得设计成单例还是比较方便的,下面给出单例的模板。


//单例模板

template <class T>
class Singleton
{
public:
static T* getInstance();    //获取单例对象
static void freeInstance(); //释放对象
protected:
Singleton(){};  //将构造函数私有或保护
private:
static T* iInstance;  //存对象
};


template <class T>
T* Singleton<T>::iInstance = NULL;


template <class T>
T* Singleton<T>::getInstance()
{
if(iInstance == 0){
iInstance = new T();
}


return iInstance;
}


template <class T>
void Singleton<T>::freeInstance()
{
if(iInstance){
delete iInstance;
iInstance = NULL;
}
}

如上的这种写法在程序中的时候你可以用具体的数据类型来代替,但是思路需要按照模板提供的这个思路来写。如果你的程序中有多个类想要设置为单例类,而同样的代码不想重复编写,我们就需要继承这个模板类,然后做一些操作,下面给出我在摸索过程中的一种方法,这种方法是不能完成任务的,但是你可以知道为什么会引出最终的那种方法了。

class MyClass:public Singleton<MyClass>
{
public:
void setAge(int age){m_iAge = age;};
int getAge(){return m_iAge;};
protected:
private:
int m_iAge;


//将构造函数私有,防止产生其他对象
MyClass(){};
};


MyClass *c = MyClass::getInstance();
c->setAge(10);
cout << c->getAge() << endl;

程序运行的时候出错了,因为我们将MyClass的构造函数私有了,在单例模板类中,iInstance=new T();这句话是无法通过的,因为在一个类外是无法访问该类的私有成员的。如果我们将MyClass的构造函数声明为public的,则类外也就可以声明对象了,这和单例的初衷又相悖了。所以利用c++的知识,我们需要将这个单例模板类声明为MyClass类的友元类,这样的话就可以做到了,现在我将MyClass类修改一下,添加一下代码

friend class Singleton<MyClass>; //将单例模板设置为MyClass的友元类


观察者模式

什么是观察者模式,如何实现观察者模式。就是被观察者含有一个数组,里边存放了所有观察者的引用,在被观察者的状态发生改变的时候,通过调用观察者的函数来通知观察者,实现了信息的传递。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

CCNotificationCenter事件监听器

CCNotificationCenter事件监听器也好,观察者模式也罢,这个东西的用处就是用在俩个类通信的时候,一个类用来发送消息,一个类用来接收消息。其实这个东西的用处是非常大的,举个例子吧。比如一片草地,会在不同的季节反应出不同的状态,春天的时候草长了出来,都是绿的,夏天的时候草长的更高了,秋天的时候又会变颜色,有篇博客事件驱动,你想象不到的强大。下面我来说说怎么用这个东西,以及用的时候要注意的一些问题。

1//将消息名定义在一个头文件中
2#ifndef _NOTIFICATION_H_
3#define _NOTIFICATION_H_
4 
5#define MY_NOTIFICATION "Hello"
6 
7#endif

CCNotification采用了单例的设计模式,使用函数addObserver来监听消息。这段代码我写在了init中。

1//第一个参数是监听消息的对象,第二个参数是消息发来的时候调用的函数,第三个参数消息的名字
2    //第四个参数消息体
3    CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
4        callfuncO_selector(TestScene::getNotification),MY_NOTIFICATION,NULL);
1//监听消息的函数
2void TestScene::getNotification(CCObject * object)
3{
4    int * i = (int *)object;
5    CCLog("HelloWorld i = %d",i);
6}

接下来就是使用post来发送消息了。

1void HelloWorld::menuCloseCallback(CCObject* pSender)
2{
3    //注意1、在发送消息之前一定要先初始化TestScene,这个时候在TestScene的init方法中已经注册了消息,也就是
4    //调用了addObserver函数,否则的话是接受不到消息的
5    CCDirector::sharedDirector()->replaceScene(TestScene::scene());
6 
7    //第一个参数是消息的名字,第二个参数是CCObject * 类型的消息值,也就是你要发送的东西
8    CCNotificationCenter::sharedNotificationCenter()->postNotification(MY_NOTIFICATION,(CCObject *)1);
9}

释放消息一般写在onExit()函数中,就是谁监听消息,谁释放消息。

1void TestScene::onExit()
2{
3    //注意最后一定要释放消息,否则内容泄露
4    CCNotificationCenter::sharedNotificationCenter()->removeObserver(this,MY_NOTIFICATION);
5    //返回值是int类型,表示释放的监听消息的个数
6    //int ret = CCNotificationCenter::sharedNotificationCenter()->removeAllObservers(this);
7    //CCLog("%d",ret);
8}


二段构建模式

大家都知道在c++中我们一般在构造函数中为对象分配内存空间然后初始化成员变量,比如我们调用了new某个东西,那么在堆上会先为对象分配内存空间,然后调用构造函数,在构造函数中完成一些初始化的工作。而二段构建模式就是将内存空间的分配和初始化分开来完成,然后调用一个静态方法来返回这个对象。就拿cocos2dx中的Sprite类来说吧,当我们调用Sprite::create()的时候内部先使用new来分配内存空间,然后调用init方法来初始化一些变量的设置。所以cocos2dx中的二段构建模式就是将new分配内存空间和init初始化内容分开来处理,而不是c++传统的做法在构造函数中初始化变量。

1Sprite* Sprite::create()
2    {
3        //分配内存
4        Sprite *sprite = new Sprite();
5        //init初始化
6        if (sprite && sprite->init())
7        {
8            //内存管理的工作
9            sprite->autorelease();
10            return sprite;
11        }
12        CC_SAFE_DELETE(sprite);
13        return nullptr;
14    }

上边就是使用二段构建模式的过程,Sprite首先调用new来分配内存空间,然后调用init函数来完成初始化的工作,顺带还做了内存管理的工作,最后返回初始化好的对象。所以看了Sprite的create方法的实现,我们也知道了应该怎么使用这个二段构建模式了吧。

第二个问题是为什么要这么用,对于c++程序员来说初始化工作不都是在构造函数中完成的吗,cocos中为何要这么做呢?这里引述一下王哲的话:“其实我们设计二段构造时首先考虑其优势而非兼容cocos2d-iphone. 初始化时会遇到图片资源不存在等异常,而C++构造函数无返回值,只能用try-catch来处理异常,启用try-catch会使编译后二进制文件大不少,故需要init返回bool值。Symbian, Bada SDK,objc的alloc + init也都是二阶段构造”。现在大家明白了吧,兼容cocos2d-iphone是一个原因,另一个重要的原因是构造函数没有返回值啊,如果加载资源图片的时候不存在怎么办,所以初始化的工作写在init函数中,这个函数返回的bool值用来判断是否初始化成功。使用这种方法还可以强化设计,想想自己写代码的时候是不是因为没有初始化某个成员变量导致了bug,这样做就是提醒你记得要在init中初始化成员变量。通过create静态函数返回的这个对象也实现了cocos2dx中的内存管理,就不用我们自己麻烦了。还有一个原因是在c++的构造函数中是不能调用虚函数的,为了调用虚函数来完成一些功能就要写在init函数中。

以上就是二段构建模式的说明了,在我们写cocos程序的时候其实不知不觉就已经在使用这个构建模式了,想一下我们一个类继承了Layer,然后使用了宏CREATE_FUNC(),这不就是create静态方法吗,在init函数中完成了初始化,整个过程就是在用这种设计模式!



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值