一:观察者Observer
观察者的设计意图和作用是: 它将对象与对象之间创建一种依赖关系,当其中一个对象发生变化时,它会将这个变化通知给与其创建关系的对象中,实现自动化的通知更新。
游戏中观察者的适用环境有:
1:UI控件管理类。当我们的GUI控件都使用观察者模式后,那么用户的任何界面相关操作和改变都将会通知其关联对象——我们的UI事件机。
2:动画管理器。很多时候我们在播放一个动画桢的时候,对其Frame有很大兴趣,此时我们设置一个FrameLister对象对其进行监视,获得我们关心的事件进行处理是必须的。
观察者伪代码:
//——
// 被观察对象目标类
Class Subject
{
// 对本目标绑定一个观察者 Attach( Observer );
// 解除一个观察者的绑定 DeleteAttach( Observer );
// 本目标发生改变了,通知所有的观察者,但没有传递改动了什么
Notity()
{
For ( …遍历整个ObserverList …)
{ pObserver ->Update(); }
}
// 对观察者暴露的接口,让观察者可获得本类有什么变动GetState();
}
//——
// 观察者/监听者类
Class Observer
{
// 暴露给对象目标类的函数,当监听的对象发生了变动,则它会调用本函数通知观察者
Void Update ()
{
pSubject ->GetState(); // 获取监听对象发生了什么变化
TODO:DisposeFun(); // 根据状态不同,给予不同的处理
}
}
//——
非程序语言描述:
A是B的好朋友,对B的行为非常关心。B要出门,此时A给了B一个警报器,告诉B说:“如果你有事,立刻按这个警报器告诉我。”。结果B在外面遇上了麻 烦,按下警报器(Update()),B就知道A出了事,于是就调查一下B到底遇到了什么麻烦(GetState()),当知道B原来是因为被人打了,于 是立刻进行处理DisposeFun(),派了一群手下帮B打架。
当然关心A的人可以不止一个,C,D可能也对A很关心,于是A这里保存一个所有关心它的人的链表,当遇到麻烦的时候,轮流给每个人一份通知。
二:单件模式Singleton单件模式的设计意图和作用是: 保证一个类仅有一个实例,并且,仅提供一个访问它的全局访问点。
游戏中适用于单件模式的有:
1:所有的Manger.在大部分的流行引擎中都存在着它的影子,例如SoundManager, ParticeManager等。
2:大部分的工厂基类。这一点在大部分引擎中还是见不到的,实际上,我们的父类工厂采用唯一实例的话,我们子类进行扩展时也会有很大方便。
单件模式伪代码:
//——
Class Singleton
{
Static MySingleton; // 单件对象,全局唯一的。
Static Instance(){ return MySingleton;} // 对外暴露接口
}
//——
三:迭代器Iterator
迭代器设计意图和作用是: 提供一个方法,对一个组合聚合对象内各个元素进行访问,同时又不暴露该对象类的内部表示。
游戏中适用于迭代器模式的有: 因为STL的流行,这个设计已经广为人知了,我们对任何形式的资源通一管理时,不免会将其聚合起来,或者List,或者Vector,我们都需要一个对其进行访问的工具,迭代器无疑是一个利器。
迭代器伪代码:
//——
// 迭代器基类
Class Iterator
{
Virtual First();
Virtual Next();
Virtual End();
Virtual CurrentItem(); // 返回当前Item信息
}
//——
// 聚合体的基类
Class ItemAggregate
{
Virtual CreateIterator(); // 创建访问自身的一个迭代器
}
//——
// 实例化的项目聚合体
Class InstanceItemAggregate : public ItemAggregate
{
CreateIterator(){ return new InstanceIterator(this); }
}
//——
四:访问者模式Visitor:
访问者设计意图和作用是: 当我们希望对一个结构对象添加一个功能时,我们能够在不影响结构的前提下,定义一个新的对其元素的操作。(实际上,我们只是把对该元素的操作分割给每个元素自身类中实现了而已)
游戏中适用于访问者模式的有: 任何一个比较静态的复杂结构类中都适合采用一份访问者。这里的“比较静态的复杂结构类”意思是,该结构类中元素繁多且种类复杂,且对应的操作较多,但类很 少进行变化,我们就能够将,对这个结构类元素的操作独立出来,避免污染这些元素对象。
1:例如场景管理器中管理的场景节点,是非常繁多的,而且种类不一,例如有Ogre中的Root, Irrchit中就把摄象机,灯光,Mesh,公告版,声音都做为一种场景节点,每个节点类型是不同的,虽然大家都有共通的Paint(),Hide() 等方法,但方法的实现形式是不同的,当我们外界调用时需要统一接口,那么我们很可能需要需要这样的代码
Hide( Object )
{ if (Object == Mesh) HideMesh(); if (Object == Light) HideLight(); … }
此时若我们需要增加一个Object新的类型对象,我们就不得不对该函数进行修正。而我们可以这样做,让Mesh,Light他们都继承于Object,他们都实现一个函数Hide(),那么就变成
Mesh::Hide( Visitor ) { Visitor.Hide (Mesh); }
Light::Hide(Visitor ){ Visitor.Hide (Light); }
我们在调用时只需要Object.Hide(Visitor){ return Visitor.Hide(Object); }
这样做的好处,我们免去了对重要函数的修正,Object.Hide(Visitor){}函数我们可以永久不变,但是坏处也是很明显的,因为将方法从对 象集合结构中抽离出来,就意味着我们每增加一个元素,它必须继承于一个抽象的被访问者类,实现其全部函数,这个工作量很大。
所以,访问者是仅适合于一个装载不同对象的大容器,但同时又要求这个容器的元素节点不应当有大的变动时才使用。另外,废话一句,访问者破坏了OO思想的。
访问者伪代码:
//——
// 访问者基类
Class Visitor
{
Virtual VisitElement( A ){ … }; // 访问的每个对象都要写这样一个方法
Virtual VisitElement( B ){ … };
}
// 访问者实例A
Class VisitorA
{
VisitElement( A ){ … }; // 实际的处理函数
VisitElement( B ){ … }; // 实际的处理函数
}
// 访问者实例B
Class VisitorB
{
VisitElement( A ){ … }; // 实际的处理函数
VisitElement( B ){ … }; // 实际的处理函数
}
// 被访问者基类
Class Element
{
Virtual Accept( Visitor ); // 接受访问者
}
// 被访问者实例A
Class ElementA
{
Accecpt( Visitor v ){ v-> VisitElement(this); }; // 调用注册到访问者中的处理函数
}
// 被访问者实例B
Class ElementB
{
Accecpt( Visitor v ){ v-> VisitElement(this); }; // 调用注册到访问者中的处理函数
}
//——
五:外观模式Fa?ade
外观模式的设计意图和作用是: 将用户接触的表层和内部子集的实现分离开发。实际上,这个模式是个纸老虎,之后我们看伪代码立刻就会发现,这个模式实在用的太频繁了。
游戏中需要使用外观模式的地方是: 这个非常多了,举几个比较重要的。
1:实现平台无关性。跨平台跨库的函数调用。
2:同一个接口去读取不同的资源。
3:硬件自动识别处理系统。
外观模式伪代码
//——
// 用户使用的接口类
Class Interface
{
// 暴露出来的函数接口函数,有且仅有一个,但内部实现是调用了两个类
Void InterfaceFun()
{
// 根据某种条件,底层自主的选择使用A或B的方法。用户无须关心底层实现
If ( XXX )
{
ActualA->Fun();
}
Else
{
ActualB->Fun();
}
};
}
// 实际的实现,不暴露给用户知道
Class ActualA
{
Void Fun();
}
// 实际的实现,不暴露给用户知道
Class ActualB
{
Void Fun();
}
怎么样,纸老虎吧,看起来很高深摸测的命名而已。
//——
六:抽象工厂模式AbstractFactory
抽象工厂的设计意图和作用是: 封装出一个接口,这个接口负责创建一系列互相关联的对象,但用户在使用接口时不需要指定对象所在的具体的类。从中文命名也很容易明白它是进行批量生产的一个生产工厂的作用。
游戏中使用抽象工厂的地方有: 基本上任何有批量的同类形式的子件地方就会有工厂的存在。(补充一句:下面代码中的ConcreteFactory1实例工厂就是工厂,而抽象工厂仅仅是工厂的一个抽象层而已。)
1:例如,在音频方面,一个音频的抽象工厂派生出不同的工厂,有音乐工厂,音效工厂。音效工厂中又有一个创建3D音效节点的方法,一个创建普通音效节点的 方法。最终用户只需要SoundFactory->Create3DNode( pFileName );就可以创建一个节点了。
2:场景对象。
3:渲染对象。
4:等等……
工厂与单件,管理器Manager关系一定是非常紧密的。
抽象工厂伪代码:
//——
class AbstractProductA {}; // 抽象的产品A基类class AbstractProductB {}; //抽象的产品B基类
// 抽象工厂基类class AbstractFactory { public:virtual AbstractProductA* CreateProductA() = 0 ;// 创建ProductA virtual AbstractProductB* CreateProductB() = 0 ;// 创建ProductB } ;
class ProductA1 : public AbstractProductA {}; // 产品A的实例1 class ProductA2 : public AbstractProductA {}; // 产品A的实例2
class ProductB1 : public AbstractProductB {}; // 产品B的实例1 class ProductB2 : public AbstractProductB {}; // 产品B的实例2
// 实例工厂1
class ConcreteFactory1 : public AbstractFactory { virtual AbstractProductA* CreateProductA() { return new ProductA1() ; } virtual AbstractProductB* CreateProductB() { return new ProductB1() ; } static ConcreteFactory1* Instance() { } // 实例工厂尽量使用单件模式} ;
// 实例工厂2
class ConcreteFactory2 : public AbstractFactory { virtual AbstractProductA* CreateProductA() { return new ProductA2() ; } virtual AbstractProductB* CreateProductB() { return new ProductB2() ; } static ConcreteFactory2* Instance() {} // 实例工厂尽量使用单件模式} ;
}
//——
客户端代码:
Void main()
{ AbstractFactory *pFactory1 = ConcreteFactory1::Instance() ;AbstractProductA *pProductA1 = pFactory1->CreateProductA() ;AbstractProductB *pProductB1 = pFactory1->CreateProductB() ;AbstractFactory *pFactory2 = ConcreteFactory2::Instance() ;AbstractProductA *pProductA2 = pFactory2->CreateProductA() ;AbstractProductB *pProductB2 = pFactory2->CreateProductB() ;}
//——