关闭

gamepackage之LoadQueue

1175人阅读 评论(0) 收藏 举报
上一篇讲了Loader,这一篇的LoadQueue,是个很头痛的东西,感觉作者出神入化的境界已经到了走火入魔的地步了,这么说是有原因的-_-||

LoadQueue的实现是继承了Array,而没用组合,以设计模式来说可能会缺少弹性,但这并非重要的地方.所以不管了.

先看构造函数
理解上简单一点的话,用一个循环就行了
for(var i=0; i<arguments.length; i++)
this[i] = arguments[i];

不过作者使用了
splice.apply( this , [ 0 , 0 ].concat( arguments ) );
splice和concat都是Array的方法,因为LoadQueue继承Array,所以也拥有这些方法.这句的结果和上面的循环是一样的.只是作者很巧妙的运用了已有的方法.
splice方法是将第三个参数起,后面每个参数作为一个元素,一个一个的插入到数组中,头两个参数是设定插入的起始位置和替换个数;
concat是联合两个数组;
apply是Function的方法,两个参数分别指定了作用域和,调用方法的参数列表.
(详细函数用法看帮助)
整句话,翻译一下就是

this.splice(0,0, arguments[0],arguments[1],....arguments[arguments.length-1]); // this指LoadQueue

接着,同样和Loader一样,用AsBroadcaster初始了LoadQueue,使其具有广播能力.


------------------------------------------------------

跳过load方法,眼前一片熟悉的on打头的方法,好像就是Loader所拥有的事件嘛,怎么在LoadQueue里面给定义出来了?看看执行了些什么吧~
看完,发现每个方法中,都广播了同名的事件,而且广播语句的写法上还很像构造函数里面splice的用法.有了上面的经验,再给他翻译一下吧.

以onLoaderStart为例:
this.broadcastMessage("onLoaderStart", arguments[0],arguments[1],....arguments[arguments.length-1]);
噢...搞了半天,原来就是在onLoaderStart方法调用后,广播onLoaderStart事件嘛,那onLoaderStart这个方法是给谁调用的呢?

回头看load方法,找到了currentLoader.addListener( this );这句
这下明白了,原来LoadQueue里面定义的那些方法,都是Loader的事件.LoadQueue成为Loader的一个监听者.

再看下onLoaderInit方法的实现,就更清晰了,在Loader确认下载完成后,首先广播LoadQueue的onLoaderInit事件,然 后把LoadQueue从当前Loader的监听者列表中去掉,节省资源,接着再执行一遍load方法,来遍历整个Queue.

------------------------------------------------------
使用Loader和LoadQueue类

有了这么两个类,就省事多了^^_
var loadQueue:LoadQueue = new LoadQueue();
var loadQueueListener:Object = new Object();
loadQueue.addListener(loadQueueListener);
loadQueueListener.onLoaderStart = function(){};
//...
loadQueue.push(new Loader(mc1,url1));
loadQueue.push(new Loader(mc2,url2));
loadQueue.push(new Loader(mc3,url3));
loadQueue.load();
可以看到确实是方便啊,但是好像有个缺陷,就是3个Loader的触发事件都是一样的,那万一我第二个Loader的onLoaderStart还想做点别的事情,怎么办?
当然,可以在loadQueueListener.onLoaderStart内部判断是否是第二个Loader,不过这么做有点费劲,如果有10个 Loader,每个Loader的onLoadStart都不一样,岂不是这个loadQueueListener.onLoaderStart要写百来 行才算完?很不容易维护.
最好的办法,就是把第二个Loader拿出来
...
var loader2: Loader = new Loader(mc2,url2);
loader2.onLoaderStart = function(){};
...
loadQueue.push(new Loader(mc1,url1));
loadQueue.push(loader2);
...
如果程序这样写了,在执行的时候,就会同时触发Loader自身的事件和LoadQueue定义的事件...而你是想只让Loader触发自定义的事件,不想执行LoadQueue的通用事件,那也很简单,只要在LoadQueue的通用事件里面写上一句话就行了
loadQueue.onLoaderStart = function( loader: Loader )
{
   if( loader.onLoaderStart != null ) return;
   // 剩下通用的执行代码   
}



本来写到这,也算完了,可是我好像还没为开头说作者走火入魔而负责~_~|.
其实,大家仔细点可以看出,作者没有把LoadQueue内监听Loader的事件方法,写成private的,这些方法显然不会被外部所用.
虽然我们知道AS2中,OOP的一些语法形同虚设,走个形式而已,不过呢,有总比没有好,至少是增加了可读性.但作者偏偏没有加private?那到底是高招呢,还是败招??.....偶们再深入分析分析.


--------------------------------------------------------

重新打开Loader类来看看.
在Loader类的构造函数中,我们看到了addListener(this);说明Loader一开始自己就在监听自己.那么我们可以像
var loader: Loader = new Loader(参数);
loader.onLoaderStart = function(){};
loader.load();
这样直接在loader对象上定义事件即可.


而LoadQueue类的构造函数中,没有addListener(this);即LoadQueue仅是一个广播者,那就需要在外部建立一个监听对象,像这样
var loaderQueue: LoaderQueue = new LoaderQueue(loader1, loader2, loader2);
var loaderQueueListener: Object = new Object();
loaderQueue.addListener( loaderQueueListener );
loaderQueueListener.onLoaderStart = function(){};
loaderQueue.load();
这样才能监听loaderQueue的事件.(和MovieClipLoader相同)

以上LoadQueue事件触发的内部执行步骤为
当前Loader开始下载时广播了Loader.onLoaderStart事件,此时,LoadQueue对象为监听者,所以执行onLoaderStart方法,由onLoaderStart方法中再广播LoadQueue.onLoaderStart事件


由于LoadQueue的事件和Loader的事件同名,又因为LoadQueue是Loader的监听者,并且事件方法还不是private的,所以直接在外部loadQueue对象上改写事件方法,也不会有任何警告提示,flash会认为你只是重写这个函数而已.
var loaderQueue: LoaderQueue = new LoaderQueue(loader1, loader2, loader2);
loaderQueue.onLoaderStart = function(){};
loaderQueue.load();
那么Loader事件广播后,就会执行这个重写的函数,所以得到的效果和LoadQueue被广播了一样.

此时LoadQueue事件触发的内部执行步骤为
当前Loader开始下载时广播了Loader.onLoaderStart事件,此时,LoadQueue对象为监听者,所以执行了onLoaderStart方法,但由于onLoaderStart在外部被重写了,所以是外部重写的程序被执行了.

这么一来,好像两者没有区别,反正该执行的事件都执行到了,但是后者却犯了一个极大的错误.那就是把LoadQueue原有的广播能力给抹杀了!
想想,如果外部重写的程序没有去执行广播的话,那LoadQueue再怎么去addListener,listener拥有的监听事件也是不会被执行的了.
而LoadQueue内有一个事件名为onLoadQueueComplete,如果也是直接建立在loadQueue对象上,那这个事件也是无论如何都不会被触发的,因为Loader内部没有这个同名事件.

所以我们可以看到,LoadQueue在设计的时候,和MovieClipLoader一样,自身并非监听者.
而上述的看似正确的调用都只是Loader和LoadQueue之间的一个巧合.这个巧合发生几率如此之高,也就是因为,Loader和 LoadQueue设计上的类似但不同一,造成人的惯性思维...却没有一个警告提示的,最主要的原因还是LoadQueue中的一些私有函数公开化.

至此,我们应该为LoadQueue内每个对应Loader的事件函数,增加private修饰,防止在外部被重写或调用.
另外,将Loader构造函数内的addListener(this);以及5个public Function属性去掉,使其也只能通过外部添加监听者,从而使Loader和LoadQueue在设计上统一
至于AS2脆弱的编译能力,我们只要清楚了原理,还是能加以避免的.

那问,为什么不让LoadQueue自己也监听自己?这样不是不行,只是这样一来,就要动大手术了,起码就要把LoadQueue的事件名字全部换成和 Loader不一样的.否则仍然用现有的程序,当当前Loader一开始下载,广播onLoadStart的时候,执行了LoadQueue的 onLoaderStart方法,但LoadQueue自身也在监听自己,广播的也是onLoaderStart,这样就形成了无限循环.

0
0

  相关文章推荐
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:104067次
    • 积分:1549
    • 等级:
    • 排名:千里之外
    • 原创:43篇
    • 转载:2篇
    • 译文:8篇
    • 评论:15条
    文章分类
    最新评论
    学之利器