这是我第二次针对甘露模型做专题了。这一次是基于最新版本的甘露模型做设计模式的探讨实现。
开篇之前,先说几句废话:
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个续篇。看回帖数来决定要不要写。