Spiga

甘露模型的装饰模式实现

2009-01-14 22:17 by 梁逸晨, 1607 visits, 收藏, 编辑

这是我第二次针对甘露模型做专题了。这一次是基于最新版本的甘露模型做设计模式的探讨实现。

开篇之前,先说几句废话:

javascript运行于浏览器之上,而设计模式一般是在大型项目中才有用武之地,这二者一结合,也便产生了一个中心,两个基本点。

所谓 一个中心稳定运行,性能至上

浏览器上面运行的程序,想要做大一点的项目,首先是要过性能这一关。而甘露模型实现了完美的内存资源节约和性能优化,是架构核心的不二之选。


所谓 两个基本点:1,架构清晰; 2,易于扩展

基于甘露模型本身的面向对象思想,完全可以用来实现GOF23,所以,在架构和扩展上既然有了设计模式做铺垫,也就不必担心什么大问题了。

本文出处:http://www.cnblogs.com/kvspas/archive/2009/01/14/1375913.html

代码的例子描述来自 terrylee 的装饰模式C#版本, http://terrylee.cnblogs.com/archive/2006/03/01/340592.html

关于装饰模式的详细解释,请看 terrylee 的装饰模式C#版本,这里我就不多详解了。

废话完毕,直接上代码:


        
//============甘露模型开始 =================================

        
//定义类的语法甘露:Class()
        //最后一个参数是JSON表示的类定义
        //如果参数数量大于1个,则第一个参数是基类
        //第一个和最后一个之间参数,将来可表示类实现的接口
        //返回值是类,类是一个构造函数
        function Class() {
            
var aDefine = arguments[arguments.length - 1]; //最后一个参数是类定义
            if (!aDefine) return;
            
var aBase = arguments.length > 1 ? arguments[0] : object; //解析基类

            
function prototype_() { }//构造prototype的临时函数,用于挂接原型链
            prototype_.prototype = aBase.prototype;  //准备传递prototype
            var aPrototype = new prototype_();    //建立类要用的prototype

            
for (var member in aDefine)  //复制类定义到当前类的prototype
                if (member != "Create")    //构造函数不用复制
                aPrototype[member] = aDefine[member];

            
//根据是否继承特殊属性和性能情况,可分别注释掉下列的语句
            if (aDefine.toString != Object.prototype.toString)
                aPrototype.toString 
= aDefine.toString;
            
if (aDefine.toLocaleString != Object.prototype.toLocaleString)
                aPrototype.toLocaleString 
= aDefine.toLocaleString;
            
if (aDefine.valueOf != Object.prototype.valueOf)
                aPrototype.valueOf 
= aDefine.valueOf;

            
if (aDefine.Create)  //若有构造函数
                var aType = aDefine.Create  //类型即为该构造函数
            else    //否则为默认构造函数
                aType = function() {
                    
this.base.apply(this, arguments);   //调用基类构造函数
                }
;

            aType.prototype 
= aPrototype;   //设置类(构造函数)的prototype
            aType.Base = aBase;             //设置类型关系,便于追溯继承关系
            aType.prototype.Type = aType;   //为本类对象扩展一个Type属性
            return aType;   //返回构造函数作为类
        }
;

        
//根类object定义:
        function object() { }    //定义小写的object根类,用于实现最基础的方法等
        object.prototype.isA = function(aType)   //判断对象是否属于某类型
        {
            
var self = this.Type;
            
while (self) {
                
if (self == aType) return true;
                self 
= self.Base;
            }
;
            
return false;
        }
;

        object.prototype.base 
= function()  //调用基类构造函数
        {
            
var Base = this.Type.Base;  //获取当前对象的基类  
            if (!Base.Base)  //若基类已没有基类
                Base.apply(this, arguments)     //则直接调用基类构造函数
            else    //若基类还有基类         
            {
                
this.base = MakeBase(Base);     //先覆写this.base
                Base.apply(this, arguments);    //再调用基类构造函数
                delete this.base;               //删除覆写的base属性
            }
;

            
function MakeBase(Type) //包装基类构造函数
            {
                
var Base = Type.Base;
                
if (!Base.Base) return Base; //基类已无基类,就无需包装
                return function()   //包装为引用临时变量Base的闭包函数
                {
                    
this.base = MakeBase(Base);     //先覆写this.base
                    Base.apply(this, arguments);    //再调用基类构造函数
                }
;
            }
;
        }
;


    
//========== 甘露模型完毕,下面是装饰者模式 ====================

    
    
//抽象基类
    var Ilog = Class(
        
{
            write: 
"this is an abstract method" //这里只能随便用个东西来代替 C# 的 public abstract void write();
        }

    );

    
//记录到文本文件
    var TextLog = Class(Ilog, 
        
{
            write: 
function() {

                alert(
"记录到文本文件!");
            }

        }

    );

    
//记录到数据库
    var DbLog = Class(Ilog, 
        
{
        write: 
function() {

            alert(
"记录到数据库!");
            }

        }

    );

    
//抽象功能扩展,所有的功能扩展都从这个类继承
    var ILogWrapper = Class(Ilog, 
    
{
        
        Create: 
function(_log) {

            
this.log = _log;
        }
,

        write: 
"this is an abstract method" //这里只能随便用个东西来代替 C# 的 public abstract void write();
        //这个write其实可以不要的,但那是托了javascript的运行机制的福气,既然是模拟OOP,就还是加上比较好。
    }

    );

    
//具体功能扩展1
    var LogErrorWrapper = Class(ILogWrapper,
    
{
        Create: 
function(_log) {

            
this.base(_log);
        }
,
        write: 
function() {
        
            alert(
"具体功能扩展:LogErrorWrapper");
            
this.log.write();//重点
        }

    }

    );

    
//具体功能扩展2
    var LogPriorityWrapper = Class(ILogWrapper,
    
{
        Create: 
function(_log) {

            
this.base(_log);
        }
,
        write: 
function() {

            alert(
"具体功能扩展:LogPriorityWrapper");
            
this.log.write();//重点
        }

    }

    ); 

    
//客户端实现
    function main() {

        
var base = new TextLog();
        
        
var e = new LogErrorWrapper(base);
        
var e2 = new LogPriorityWrapper(e);
        
        e2.write();

    }


    main();
   

 

 

 装饰模式是为了避免随着功能需求的增多而发生子类膨胀。在javascipt中,我们其实可以有更加优雅的写法:直接扩充class的property,三两句话就解决问题了。为什么还要用这种简直是茴字的四个写法一样的代码呢?这就需要从问题的根本上谈起:既然这是描述设计模式,而设计模式本来就是为了解决大型的、长期可维护、可扩充的代码而存在的,那么我们首先要关注的就是大型项目的长远问题。

长远问题的第一点:随着需求的不断增多,基类的property也不断增多,很多时候一个对象的生存期仅仅是做一件事情(仅需要一个property),而它在创建期上却加载了100个property。

长远问题的第二点:直接使用逐渐增加的property扩展,很难说会不会今天喝醉了搞了个闭包进来,没人发觉,明天弄了个依赖全局对象的东西进来,也没人发觉。半年之后才发觉程序变得很慢很难维护的时候,这种隐式杀手是不可能找得出来的。

这就是大型长期项目不能简单使用原型扩展的原因。

好了,全文完,理论上还存在22个续篇。看回帖数来决定要不要写。

 

Add your comment

6 条回复

  1. #1楼 stan0714[未注册用户]2009-01-14 22:24
    YY 的,没有看懂,就不能画个图吗?
    这样更直观
     回复 引用   
  2. #2楼[楼主] 梁逸晨      2009-01-14 22:29
    关于装饰模式的详细解释,请看 terrylee 的装饰模式C#版本,这里我就不多详解了。
     回复 引用 查看   
  3. #3楼 李战      2009-01-15 08:18
    2009年广告词:我要喝甘露!
     回复 引用 查看   
  4. #4楼 6640[未注册用户]2009-01-15 09:07
    理论是好的,但是非得把面向对象的特性加到脚本语言里去,还是有点别扭!
     回复 引用   
  5. #5楼 痴情客      2009-01-15 10:45
    即使用javascript完成大的项目前端,也不需要这么做。

    架构清晰,易于扩展并不是靠什么模式的,关键看自己的代码怎么去组织

    虽然javascript很灵活,可运用它特性去实现这些模式,但从事实来看,只会让代码更晦涩难懂,你可以思考下。

     回复 引用 查看   
  6. #6楼 痴情客      2009-01-15 10:46
    一个javascript完成的简单例子:

    http://www.scriptlover.com/controls/context/
     回复 引用 查看