基于 mixin 的组件设计

思路主要来源于 YUI3 component infrastructure ,利用 mixin 机制模拟 c++ 的多继承 机制,多继承争论很多,但是谨慎使用确实会带来设计的简洁明了,避免采用 Extjs 式的单继承所带来的深层结构以及由于功能聚集导致的巨型类,且功能不能在多个组件中共享。

 

ppt @ slideshare


多继承:


主要依据 c++ 规范,当多继承时,注意构造函数和析构函数的合适触发顺序,举例:

 

弹窗

 

用 c++ 多继承来设计弹窗组件,分析弹窗的功能,将弹窗理解为


1.是一个弹出层

2.具备头,体,尾组成部分

3.具备拖拽功能

4.可以被关闭


伪代码:

 

class StdMod {
    private:
          header;
          body;
          footer;
}

class Close {
    private:
        closable;
}

class Drag {
    private:
        draggable;
        handlers;
}

//待定义
class Overlay{
//...
}

class Dialog : Overlay,StdMod,Close,Drag {
}

 

弹出层

 

而弹出层又是一个复杂的东西,是很多功能的聚集体


1.是一个方形可渲染容器

2.可以被定位,隐藏

3.可以具备背景遮罩功能


伪代码:

 

class Box {
  private:
     width;
     height;
}

class Position {
   private:
       x;
       y;
       visible;
}

class Mask {
     private:
          maskable;
}


class Overlay : Box,Position,Mask{
}

 

 

弹层的生成和销毁


生成:


首先初始化基类的成员并执行基类的构造器,多个基类的构造器执行顺序,根据继承时的申明顺序指定,然后执行底层类的构造器体。


即:绑定用户值给对应的属性并执行 Box ,Position ,Mask 的构造器,然后执行 Overlay 的构造器。


销毁:


和构造器的执行顺序完全相反,只不过变成了析构器。


javascript 模拟:


而对于 javascript (有人说和c++同级... ),当然没有这些完善的对象生命周期管理,一切都得靠自己,除此之外还要加上不少的开发约定。我的实现是:这些所有构造与析构的依赖顺序全部扔给最顶层的类 Base 处理,并且由于原型链的存在使得 js 天生只能实现单继承( SI ),那么在多继承的情况下,除了第一个类,其他类只能通过 mixin 的方式混给最终类。


类的产生:


通过 Base.create(cls,[clsExt1, clsExt2,... ],prototype,staticProperty) 产生一个新构造器,该构造器的继承链上一个构造器是 cls (cls必须继承或本身就是 Base),clsExt 为扩展类数组,通过 mixin 将自己的代码混合在最终类,并将扩展类集合的构造器挂靠在新生成的类上面。

 

 

结构:

 

基于 mixin 的组件设计



类的实例化:


当实例化时,统一委托 Base 的构造器就行,主要进行:


1.从顶层类开始,执行挂靠在顶层类上的扩展类构造器方法。


2.然后执行顶层类的 init 原型方法


3.依次沿继承链向下执行 1,2 步

 


类实例的销毁化:

 

 


当调用实例的 destroy 方法时,委托 Base 的原型 destroy 方法进行沿继承链的销毁工作,主要进行:


1.执行当前构造器原型的destructor方法

2.调用挂靠在当前类的扩展类构造器原型的__destructor方法,注意顺序和实例化时相反

3.依次沿继承链向上执行1,2 步。

 

基于 mixin 的组件设计

 

 

最终代码:


则Overlay的最终代码为:

 

 var Overlay = Base.create(Base,[S.Ext.Box,
        S.Ext.ContentBox,
        S.Ext.Position,
        S.Ext.Shim,
        S.Ext.Mask], {

        init:function() {
            S.log("Overlay init");
        },
        destructor:function(){
            S.log("Overlay destructor");
        }
});
 


初始化顺序:


box init
contentbox init
position init
shim init
mask init
Overlay init

销毁顺序:

overlay destructor
mask __destructor
shim __destructor
position __destructor
contentbox __destructor
box __destructor


更复杂的 Dialog 可打开 firebug 查看 : 这里


组件的UI周期管理:

上面只是介绍了支持组件最基本的多继承类模拟,而对于UI组件,还要涉及

1.DOM 节点生成或直接从 markup 读取(renderUI)
2.绑定客户端事件(bindUI)
3.UI 变化和组件对应属性同步(syncUI)

这里基本上和 yui3 保持一致,约定主组件各个阶段取名:

renderUI,bindUI,syncUI

而附加组件取名:

__render,__bindUI,__syncUI

属性与 UI 同步采用 attribute 机制。


//用户扩展多继承的 UIBase 组件
function Position() {}
Position.ATTRS = { x:{}, y:{}, xy:{} }
Position.prototype = {
  __renderUI: fn,
  __bindUI: fn,
  __syncUI: fn,
  _uiSetX: fn,
  __destructor: fn
}
 

小结:

世界很复杂,完美的类抽象很难

适度继承+ 选择性mix

约定优于配置

好处:可复用性高,整体维护方便

不足:多继承会造成潜在的命名空间冲突,门槛稍高


 

 

 

 

 

 

你可能感兴趣的:(JavaScript,UI,prototype,Firebug,ext)