使用设计模式来提高程序库的重复利用性是大型程序项目开发必须的。但是在“四人帮”的设计模式概述中提到了23种标准设计模式,伍但难以记住,而且有些设计模式更多的适用于应用程序开发,对游戏项目引擎设计并没有很多的利用价值。根据经验,精挑细选后,笃志在这里记录一些自认为有利用价值的设计模式,以便之后自己设计时使用。 一:观察者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:大部分的工厂基类。这一点在大部分引擎中还是见不到的,实际上,我们的父类工厂采用唯一实例的话,我们子类进行扩展时也会有很大方便。单件模式伪代码://-------------------------------------------------------------------------------------------------------{ 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 ){ … }; }// 访问者实例AClass VisitorA{ VisitElement( A ){ … }; // 实际的处理函数VisitElement( B ){ … }; // 实际的处理函数}// 访问者实例BClass VisitorB{ VisitElement( A ){ … }; // 实际的处理函数VisitElement( B ){ … }; // 实际的处理函数}// 被访问者基类Class Element{ Virtual Accept( Visitor ); // 接受访问者}// 被访问者实例AClass ElementA{ Accecpt( Visitor v ){ v-> VisitElement(this); }; // 调用注册到访问者中的处理函数}// 被访问者实例BClass 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 Produ?tA2 : public AbstractProductA {}; // 产品A的实例2 class ProductB1 : public AbstractProductB {}; // 产品B的实例1 class ProductB2 : public AbstractProductB {}; // 产品B的实例2// 实例工厂1class ConcreteFactory1 : public AbstractFactory { virtual AbstractProductA* CreateProductA() { return new ProductA1() ; } virtual AbstractProductB* CreateProductB() { return new ProductB1() ; } static ConcreteFactory1* Instance() { } // 实例工厂尽量使用单件模式 } ; // 实例工厂2class ConcreteFactory2 : public AbstractFactory { virtual Ab?tractProductA* 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() ; }//------------------------------------------------------------------------------------------------------- |
=======================================================
Flash中oop的设计模式
人问我flash的as应该怎么写,我可以很负责任地告诉他,想怎么写就怎么写,因为as以及flash内部的构成模式决定了它的高度自由化。理论上来说,用按钮的on事件,加上stop(),play(),gotoAndStop(),gotoAndPlay(),就可以实现一个flash里大部分的逻辑关系,而且源代码简单易懂。但是大多数人不会这么做,是因为这种方法实在太让人敬佩。稍有常识的程序员都会知道面对对象与面对过程的区别。Flash 的编程虽然只是以脚本的形式出现,并且还很不完善,比如,没有多继承,但已经初步体现了oop的思想。这篇文章现在总结一下flash中面对对象的设计模式问题,以及一些自创的思路。
设计模式是美国一位建筑大师(同时也是信息工程师,画家,机械工程师…的)克里斯蒂安.亚历山大首先提出来的,很快被软件界的技术员们所接受推广,成为软件工程里至高无上的法则之一(有兴趣的人可以找他的《建筑的永恒之道》一书看看,相信会受益非浅)。简单地说就是在面对对象的基础上,包括面对对象,把要设计的整体的各个部分模式化,层次化,细粒度化,高度复用化,可控化,人性化。其中至高无上的原则是建立在需求的基础之上,也就是说,无论做什么,人的需求要放在第一位考虑,从这个角度考虑整个系统是否足够合理。这门学问是非常有趣的,尤其在flash中,可以应用到很多很好玩的实例中去。下面我按照一些通用的设计模式,举例说明,有错误的地方,敬请高手指正: 1.抽象工厂模式(Abstract Factory)食堂里吃的东西很多,而我只想吃一样,那么食堂这个概念对我来说就是个抽象工厂,每个窗口可以看成它的一个具体实现,我要做的就是,去食堂,找到那个窗口,从窗口里买我要吃的东西。举例:flash前台与asp后台的交互,访问某个动态页面,从数据库里取出需要的数据,通常的做法是在后台就把数据集解析成xml字符串,再送给swf。每个业务逻辑模块,所取出的数据结构,也就是xml的结构是不一样的,我们要针对各个具体的业务逻辑,对相应的xml字符串解析,转换成可供显示的数组。也要把flash里文本输入的内容转换成 xml字符串,提交给后台也面AbstractFactory.as 程序代码
//抽象工厂的接口 Interface AbstractFactory{ //生成xml解析工厂的具体实现 function createXmlParseFactory(); }
程序代码
//生成解析读入的xml的对象的工厂 class XMLParserGetFactory implements AbstractFactory.{ var xmlParser; function XMLParserGetFactory(str:String){ //生成解析器的具体实现,在后面会提到 } function createXmlParser(){ return xmlParser; } }
程序代码
//生成解析输出的xml的对象的工厂 class XMLParserPostFactory implements AbstractFactory.{ var xmlParser; function XMLParserPostFactory(str:String){ //生成解析器的具体实现 } function createXmlParser(){ return xmlParser; } }
程序代码
//生成对留言板的留言列表解析的工厂 var xmlParser=new XMLParserGetFactory(“xmlParseGuestbookList”) xmlParser= XMLParserGetFactory. createXmlParser()
程序代码
//生成解析读入的xml的对象的工厂 class XMLParserGetFactory implements AbstractFactory.{ var xmlParser; function XMLParserGetFactory(str:String){ //如果要求留言板列表解析器,就生成一个 if(str==” xmlParseGuestbookList”){ xmlParser=new xmlParserGuestbookList(); } } function createXmlParser(){ //返回所要求的解析器 return xmlParser; } }
程序代码
xmlParser= XMLParserGetFactory. createXmlParser(xml,arrayID,arrayTitle);
程序代码
Core.as //内核 class Core { var strucGlobalParam:ConfigVariables; //站点信息 var xmlConfig:XML; //站点信息的xml化对象 var ArrayStructureInitial:Array; //用来提供给loadObject对象的数组 var ArrayForBtn:Array; //用来初始化导航条组件的数组 var objInitial:loadObject; //读取影片的对象 var objMessageMap:MessageMap; //消息映射组件 …… }
程序代码
MessageMap.as //消息映射类 class MessageMap extends Object { var Message:String; var MessageWatcher:Function; var Target; var MessageList:Array; var Num_Msg:Number; function MessageMap() { Num_Msg = 0; MessageList = new Array(); Message = "HANG_UP"; MessageWatcher = function (prop, oldVar, newVar, Param) { for (var i = 0; i<Num_Msg+1; i++) { if (newVar == MessageList[0]) { MessageList[1].apply(MessageList[3], MessageList[2]); if (!MessageList[4]) { MessageList.splice(i, 1); Num_Msg--; i-=1; } } } }; this.watch("Message", MessageWatcher, "test"); } function SendMessage(Msg:String, mc:MovieClip) { Message = Msg; } function UpdateMessageMap(Msg:String, objFunction:Function, ArrayParam:Array, objRefer,IsMultiUsed:Boolean) { MessageList[Num_Msg] = new Array(); MessageList[Num_Msg][0] = new String(); MessageList[Num_Msg][0] = Msg; MessageList[Num_Msg][1] = new Function(); MessageList[Num_Msg][1] = objFunction; MessageList[Num_Msg][2] = new Array(); MessageList[Num_Msg][2] = ArrayParam; MessageList[Num_Msg][3] = objRefer; MessageList[Num_Msg][4] = IsMultiUsed; Num_Msg++; } function DeleteMessageMap(objRefer) { for (var i = 0; i<Num_Msg; i++) { if (MessageList[2] == objRefer) { MessageList.splice(i, 1); Num_Msg--; } } } } class SubTemplateMovie extends BaseMovie { var MovieRemoveFunction:Function; function SubTemplateMovie() { this.stop(); MovieStartFunction = function () { Lock(); this.play(); }; MovieEndFunction = function () { Lock(); this.play(); }; MovieRemoveFunction = function () { this.stop(); SendMsg("SUB_TEMPLATE_REMOVED", this); _parent.unloadMovie(); }; MovieMainFunction = function () { stop(); SendMsg("SUB_TEMPLATE_OPEN", this); }; UpdateMessage("LOADING_BAR_OVER", MovieStartFunction, null, this, false); UpdateMessage("BACK_TO_INDEX", MovieEndFunction, null, this, false); } }
程序代码
//基类影片 /所有影片的原始类,一切影片的父类都继承此类而来 class BaseMovie extends MovieClip { var isLocked:Boolean; //初始类开始影片函数 var MovieStartFunction:Function; //初始类影片主功能函数 var MovieMainFunction:Function; //初始类结束影片函数 var MovieEndFunction:Function; var GlobalParam //初始类构造函数 function BaseMovie() { } // //发送消息 function SendMsg(Msg:String, Mc:MovieClip) { _root.objCore.objMessageMap.SendMessage(Msg, Mc); } //添加消息映射 function UpdateMessage(Msg:String, MsgMapFunction:Function, ArrayParam, obj, IsMultiUsed) { _root.objCore.objMessageMap.UpdateMessageMap(Msg, MsgMapFunction, ArrayParam, obj, IsMultiUsed); } //删除消息映射 function DeleteMessage(obj) { _root.objCore.objMessageMap.DeleteMessageMap(obj); } function GetGlobalParam() { GlobalParam=_root.objCore.strucGlobalParam; } }
程序代码
//下级模板影片类 class SubTemplateMovie extends BaseMovie { var MovieRemoveFunction:Function; function SubTemplateMovie() { this.stop(); MovieStartFunction = function () { Lock(); this.play(); }; MovieEndFunction = function () { Lock(); this.play(); }; MovieRemoveFunction = function () { this.stop(); SendMsg("SUB_TEMPLATE_REMOVED", this); _parent.unloadMovie(); }; MovieMainFunction = function () { stop(); SendMsg("SUB_TEMPLATE_OPEN", this); }; UpdateMessage("LOADING_BAR_OVER", MovieStartFunction, null, this, false); UpdateMessage("BACK_TO_INDEX", MovieEndFunction, null, this, false); } }
程序代码
//Cs文件里的解析函数 Class DataModel.BlogMsgs{ … Public DataSet parseXML(string strXml){ DataSet ds=new DataSet(); //。。把xml装载到DataSet 里 Return ds } … }
程序代码
//获取全局变量 function GetGlobalParam() { GlobalParam=_root.objCore.strucGlobalParam; }
程序代码
//滚动条组件 function BindTo(mc,type:String,intMcHeight:Number,yinitial:Number){ ScrollType=type; if(type=="TXT"){ scrollTxt=mc; } if(type=="MC"){ initialY=yinitial; McHeight=intMcHeight; scrollMc=mc; } } function Scroll() { if(ScrollType=="TXT") this.onEnterFrame = function() { scrollTxt.scroll = scrollTxt.maxscroll*mcBlock._y/(BgLength-BlockLength*3/2) }; if(ScrollType=="MC"){ this.onEnterFrame=function(){ if(scrollMc._height>McHeight){ scrollMc._y=initialY-(scrollMc._height-McHeight)*mcBlock._y/(BgLength-BlockLength*3/2)} } } } [code] 备注:这也是常见模式,在flash的逻辑控制里尤其随处可见 17.装饰模式(Decorator)在食堂吃饭,没筷子怎么行?我是从来不带饭盆的。师傅很人性化,每个窗口都放着一大把筷子,随用随拿。 这个模式如果用好,有的地方可以很省力。比如,我网站里的滚动条: ScrollBar.as [code] //滚动条组件 class ScrollBar extends BaseMovie { var BgLength:Number; var BlockLength:Number; var mcBlock:MovieClip var Width:Number; var ScrollType; var scrollTxt:TextField; var scrollMc:MovieClip; var McHeight:Number var initialY:Number function ScrollBar() { } function InitialScrollBar(BgLength, BlockLength) { this.BlockLength = BlockLength; this.BgLength = BgLength; } function BindTo(mc,type:String,intMcHeight:Number,yinitial:Number){ ScrollType=type; if(type=="TXT"){ scrollTxt=mc; } if(type=="MC"){ initialY=yinitial; McHeight=intMcHeight; scrollMc=mc; } } function Scroll() { if(ScrollType=="TXT") this.onEnterFrame = function() { scrollTxt.scroll = scrollTxt.maxscroll*mcBlock._y/(BgLength-BlockLength*3/2) }; if(ScrollType=="MC"){ this.onEnterFrame=function(){ if(scrollMc._height>McHeight){ scrollMc._y=initialY-(scrollMc._height-McHeight)*mcBlock._y/(BgLength-BlockLength*3/2)} } } } function ScrollMc() { } function StopScroll() { this.onEnterFrame=null; } function Reset(){ mcBlock._y=0; } }
程序代码
//我的Blog class Blog extends BaseMovie { //blog第一界面,包括日记列表,日历,最近留言 var mcBlogList: mcBlogList; //blog第二界面,包括日记全文,回复,对该日记的留言。 var mcBlogDairy:MovieClip; var currentState:String; var currentDairyID:Number; function blog(){ } }
程序代码
//blog第一界面 class mcBlogList extends BaseMovie { //最近留言 var recentMsgs:blogMsgsSC; //日记列表 var blogList:BlogList; //日历 var calendar:CalenderForBlog; } mcblogDairy.as //blog第二界面 class mcBlogDairy extends BaseMovie { //日记全文显示 var BlogDairy:BlogDairy; //留言板 var GuestBook:BlogInputMsg; //留言列表显示 var BlogMsgs:BlogMsgs; }