ActionScript3中的插件模式开发(三):插件管理器

上一篇中,我们创建了视频播放器的主体。大家可以看出,开发主体部分和平时做应用并无二致。确实如此,插件模式在主体阶段和别的开发模式基本一样;直到需求变化,必须增加功能的时候,才要考虑是否引入插件模式。

换句话说,我们大可以按照最熟悉的方式开发产品,直到不同应用场景下对功能的需求差异很大,让我们觉得适用插件模式了,再开始重构产品。

OK,继续。接下来,插件管理器(PluginManager,后面简称PM)登场。

PluginManager

PM负责管理所有插件的生命周期,即是说,插件的创建、销毁都应该由PM操作。在本案例中,PM负责读取配置信息,创建所需插件。

实际生产中(尤其是本例)我很少“销毁插件”。“插件模式开发”的目的并非创造一套支持各种外挂插件的系统(当然不是不能),其中的插件和网游中的外挂或者浏览器的插件的设计方向完全不同:

  1. 开发者为某个应用场景所开发的功能
  2. 这个功能只在某个应用场景中不可或缺,在其它时候没必要存在
  3. 应用场景的分割非常明显、明确、几乎不会出错
  4. 不能,也没准备将选择插件的能力交给用户

所以这个模式下的插件创建后保持运行即可。几乎不存在停用和销毁。

可惜的是,ActionScript中无法遍历包中所有类,不然的话直接实例化所有插件就行了。于是我只能退而求其次,给每个插件定义一个pid作为标识符,然后在PM中保存“pid-类”的Map,通过枚举的方式创建插件。这样做虽然也不复杂,但多少还是增加了维护成本。

反复简化后,本例中的PM将会非常简单。不过在动手写代码之前,还有一个问题需要我们做出选择。

内嵌插件和后加载插件

Flash最后都要打包成**.swf**文件,项目大的时候甚至会拆分成多个.swf文件。既然我们已经从代码结构上将插件从主体当中独立,这时我们就必须做出选择:将插件打包进主体;或者先加载主体,再加载插件。

二者各有优劣。通常来说,如果是页游,我倾向于后者;在本案中,我准备用前者。

页游的资源很多,需要花大量的时间加载,所以用户通常不会在意多加载一个文件。另外,页游中插件模式主要用来支持活动,那么无活动不加载,什么活动加载什么插件就成了自然而然的选择。

视频播放器连皮肤带逻辑也就几十K,一个插件多则十几K,少则只有几K,如果用后加载,会增加很多加载成本。所以就像小图标要用CSS Sprite一样,开发播放器时我也准备把主体和插件打包到一起发布。

两种策略的实现

打包到一起比较容易实现,我们甚至可以不要插件管理器:既然插件创建后就不再移除,并且随同主体一起发布,那直接在文档类里初始化即可。不过我并不推荐这种做法。基于之前的定义:

主体……并不知道插件怎么使用,也不关心。

以及Flash的编译打包方式(后面会有一章专门讲到),我认为文档类还是应该干干净净只包含播放逻辑比较好。插件使用专门的配置文件,结合getDefinitionByName,找得到的就创建比较好。

然后说下后加载。(整个系列完成后,我可能找个时间把后加载的实现补上)(毫无疑问,这是个坑),这里简单介绍几个要点:

  1. 首先,主体编译时不要打包到插件,这需要PM里只能包含插件接口,主体的任何地方都不要import插件类
  2. 同时,编译插件时也注意不要打包到主体,确保物理分割干净
  3. 加载完成后,可以采用前面类似的逻辑创建插件实例
  4. 多个插件打包到一起也是个不错的选择

代码时间

综合之前的讲述,本例中的PM将暂时实现以下功能:

  1. 初始化时,创建所有已存在插件
  2. 将主体三部分暴露给所有插件

写成代码非常简单

public static var PLUGIN_MAP:Object = {
  ''opening-ad'': ''com.meathill.Player.plugin.OpeningAD'',
  ''more'': ''com.meathill.Player.plugin.More'',
  ''stat'': ''com.meathill.Player.plugin.Stat''
};

public function createAllPlugin():void {
  for (var pid in PLUGIN_MAP) {
    var klass:Class = getDefinitionByName(PLUGIN_MAP[pid]) as Class;
    if (klass) {
      createPlugin(pid, klass);
    }
  }
}

总结

本章中我分析了插件管理器的设计,和插件的部署方式。并且为本案例选择了比较合适的做法。

下一章中,我会解析插件基类的做法。