单例模式(Singleton)确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用
为什么要在FLASH中使用单例模式?
首先我们明白,"单例"-即在软件运行时只提供一个实例,大家知道在面向对象编程中,实例化一个对象就意味着需要在内存中开辟一单元供这个对象的实例使用.而经常的,我们编写的一个类或函数需要在系统的很多地方被引用,不得已我们每次都需要去获取这个类的实例以供使用.以至于我们要不断的去new一个对象,如此一来,不但给系统增加负担,而且会给用户带来坏的使用体验-.
就象我们制作一个工具,我们希望完成后,在以后随时随地我们都可以很方便地拿出这个工具来协助我们工作.而不是每次需要使用这个工具的时候都需要花时间花力气去重新制作这个工具.
在FLASH中使用单例模式的目的即是如此.但是首先需要明确:
1,这个工具是我们经常使用的.
2,这个工具必须可以灵活使用,而不是就单针对着特定的某一件工作.正确的说,这个工具可以应付我同一类型的一系列工作,而不是异想天开的替我解决所有问题.
3,不要让其他人使用这个工具,确保这个工具的专利属于我.
在FLASH中什么情况适合使用单例模式?
在具备以下的需求时,我们可以考虑使用单例模式:需要一个通用的业务逻辑处理器来处理同一类型的工作任务,需要一个数据访问对象来处理一系列的外部数据交互工作,前提是使用一致的数据访问手段.
由于FLASH是基于客户端的软件,客户端基本上就是一个人在使用,所以我们可以忽略通常在软件设计中需要考虑的线程并发问题(在B/S,C/S架构的系统中,通常这些任务由服务器来做).而且,虽然FLASH本身是多线程的,但是它并没有允许我们编程控制线程.
因此,我们在FLASH中使用单例模式的好处就是:节约系统开销,增大代码重用性.其实最大的好处就仅是节约系统开销而已.至于代码重用我们可以基于自身对软件设计的经验使用各种方式做到.
一个基本的单例模式:
classSingletonClass{
privatestaticvarsingletonClass:SingletonClass=null;
privatefunctionSingletonClass{}
publicstaticfunctiongetInstance():SingletonClass{
if(singletonClass==null){
SingletonClass.singletonClass=newsingletonClass();
}
returnsingletonClass;
}
}
在第一行代码.我们申明了一个静态(static)的变量,他的类型为SingletonClass类本身.既然申明的变量为static,那么在程序运行期间这个变量将会长驻在内存中而不会自动销毁
第二行代码,我们写了一个空的构造函数,而且它是私有的(private),这意味着这个类不可以在外部被实例化,这也正是我们的目的.(我们专利的工具将不会被外人模仿使用)
在 getInstance方法中,我们通过判断singletonClass的值是否为null来取决是否需要实例化singletonClass.第一次将singletonClass实例化以后,之后将不需要再实例化singletonClass.最后,这个方法将会返回一个SingletonClass实例的引用-singletonClass
看到这里,相信大家都已经明白单例模式的构成原理
下面我们来做一个应用单例模式的DEMO
我们将做一个用户信息查询的模块,应用单例模式主要的目的是保证在用户活动过程中,相关用户信息的外部数据调用工作只做一次.
考虑到设计良好的应用结构,我们将会稍微在单例模式的原则上做一些改动.我们将这一应用分为:用户模型,业务逻辑控制器,FLASH视图三部分,数据访问对象将会由FLASH内置的XML对象来充当(其实这种架构是参考常用的B/S软件结构来设计,从实际B/S系统结构的原则上讲,整个FLASH应用程序都只将作为表示层).
代表用户实体的类
MemberEntry.as
classcom.hizon.domain.MemberEntry{
privatevarid:Number;
privatevaremail:String;
publicfunctiongetId():Number{
returnid;
}
publicfunctionsetId(id:Number):Void{
this.id=id;
}
publicfunctiongetEmail():String{
returnemail;
}
publicfunctionsetEmail(email:String):Void{
this.email=email;
}
}
member.xml
xmlversion="1.0"encoding="gbk"?>
<root>
<id>1id>
<email>bhlove@gmail.comemail>
root>
负责数据访问的类
MemberDao.as
importcom.hizon.domain.MemberEntry;
classcom.hizon.dao.MemberDao{
privatestaticvarmemberEntry:MemberEntry=null;
privatefunctionMemberDao(){}
publicstaticfunctioninitMemberEntry(memberId:Number,event:Function):Void{
if(MemberDao.memberEntry==null){
varmemberProfileDoc:XML=memberProfileDoc=newXML();
memberProfileDoc.ignoreWhite=true;
memberProfileDoc.load("xml/member.xml");
memberProfileDoc.onLoad=function(success){
MemberDao.memberEntry=newMemberEntry();
MemberDao.memberEntry.setId(memberProfileDoc.firstChild.childNodes[0].firstChild.nodeValue);
MemberDao.memberEntry.setEmail(memberProfileDoc.firstChild.childNodes[1].firstChild.nodeValue);
event.call(MemberDao,MemberDao.memberEntry);
return;
}
}
event.call(MemberDao,MemberDao.memberEntry);
}
}
在Flash中进行用户信息打印
importcom.hizon.dao.MemberDao;
importcom.hizon.domain.MemberEntry;
MemberDao.initMemberEntry(1,init);
functioninit(mb:MemberEntry){
trace(mb["email"]);
}
在这里,我们使用了Function对象的call方法,也许你会问,为什么不在MemberDao里直接返回一个MemberEntry对象?
原因是这样的,在FLASH中的所有基于http协议的外部数据调用工作都是异步进行的(而且,FLASH没有提供线程控制编程).这样直接导致的结构就是:当数据调用还没完成的时候,AS脚本已经获取了return值,当然,这个值会是null
所以,我认为最好的方法就是将函数作为参数传递给方法,由方法在完成XML数据调用后执行目标函数.如果大家有更好的方法,希望不啬指教.
最后将一下我的设计思路吧
基于面向对象的设计原则(仿照JAVA设计模式).我使用了MemberEntry这个类作为用户的实体参照对象.这样在以后更多的业务逻辑中,如果我想获取/更新用户资料,我只需要针对这个对象进行get/set方法操作.然后由专门的类根据需求负责处理MemberEntry
例如:
负责向服务器提交数据的类
classcom.hizon.web.Poster{
publicfunctionpost(url:String,entry:Object,listener:Function){
varposter:LoadVars=newLoadVars();
varresult_v:LoadVars=newLoadVars();
for(varnameinentry){
poster[name]=entry[name];
}
poster.sendAndLoad(url,result_v,"POST");
result_v.onLoad=function(success:Boolean){
if(success){
listener.call(Poster);
}
}
}
}
这样,我只要将标准格式的实体对象作为参数交由Poster类处理,即可完成向服务器提交数据更新的工作.而不需要在多处编写POST代码.
其次,我使用了单例模式的原则,保证外部数据调用工作只做一次
最重要的,我把代码根据业务逻辑进行了分离.使各个部分都能够各司其职,保证了程序结构的清晰,增大了代码可重用性