JavaScript设计模式-装饰器、代理、AOP

装饰器模式(Decorator)

装饰器模式是一种保持接口一致,同时强化对象、函数功能的设计方法。装饰很好理解,女士们出门前要给自己化化妆,这就是一种装饰——人还是那个人(保持接口一致),但是样子变了不少(强化了功能)。下面先上个传统装饰器模式的例子吧(Java版):

//这是一个抽象的女士
public abstract class Woman{
    //女士都会抛媚眼
    public abstract void leer();
}
//这位女士是一个漂亮女孩
public class BeautifulGirl extends Woman{
    public void leer(){
        System.out.println("漂亮女孩抛媚眼");
    }
}
//女士装饰器
public abstract class WomanDecorator extends Woman{
    private Woman woman;
    public WomanDecorator(Woman woman){
        this.woman = woman;
    }
    //装饰之后当然还会抛媚眼
    public void leer(){
        this.woman.leer();
    }
}
//化妆当然是女士必备的装饰利器
public class  WomanMakeUp extends WomanDecorator {
    public WomanMackUp(Woman w){
        super(w);
    }
    public void leer(){
        System.out.println("化妆之后眼睛好漂亮");
        super.leer();
        System.out.println("好多男生拜倒在石榴裙下");
    }
}
//这是一个漂亮女孩,但是没化妆
Women girl = new BeautifulGirl();
girl.leer();    //输出:漂亮女孩抛媚眼
//但是没人理...

//于是化妆了一下...
girl = new WomanMakeUp(girl);
girl.leer();    //化妆之后再抛媚眼
//输出:
//化妆之后眼睛好漂亮
//漂亮女孩抛媚眼
//好多男生拜倒在石榴裙下

虽然Woman还是那个Woman,但是MakeUp之后就强大了很多,化妆的威力是多么的强大。当然,除了化妆之外,我们还可以创造出更多的装饰方法,例如穿漂亮衣服、戴首饰、瘦身、美甲等等,想多漂亮就多漂亮。

上面这种装饰器是基于Class的方法,然而对于Class并不是JavaScript的核心概念,JavaScript要实现装饰器模式,除了模仿传统Class的实现方式外,还有自己另外的一套。在JavaScript中,函数是一级公民,是对象,可以作为值被传递,我们也可以利用这一特性来设计js的装饰器:

var decorateFun = function(orig,before,after){
    var Decorator= function(){
        var args = arguments,result,i;
        if(before && typeof before == 'function'){
            //前置方法可对参数进行修改
            args = before.apply(this,args) || args;
        }
        result = orig.apply(this,args);
        if(after && typeof after == 'function'){
            //后置方法可对结果进行修改
            result = after.apply(this,[result,args])||result;
        }
        return result;
    }
    //由于函数也是对象, 因此要将其属性都复制过来
    for(i in orig){
        if(orig.hasOwnProperty(i)){
            Decorator[i] = orig[i];
        }
    }
    Decorator.prototype = org.prototype;
    //保留原始函数对象, 以备复原
    Decorator.originalFunction = orig;
    return Decorator;
}

//这是一个漂亮女孩
var girl = {
    leer: function(){ alert('漂亮女孩抛媚眼'); } 
}
//给女孩打扮一下
girl.leer = decorateFun(girl.leer,
    function(){ alert('化妆之后眼睛好漂亮'); },
    function(){ alert('好多男生拜倒在石榴裙下') }
)
//女孩开始抛媚眼了
girl.leer();
//输出:
//化妆之后眼睛好漂亮
//漂亮女孩抛媚眼
//好多男生拜倒在石榴裙下

这里主要利用了Function对象的apply方法,对原有方法进行了劫持,从而给方法添加新的功能。

 

代理模式(Proxy)

其实代理模式我是不打算详细说的,因为代理模式的形式和装饰器模式很像,区别在于装饰器是增强原对象的功能,而代理是控制原对象的功能,在js的实现方式还是利用apply进行方法劫持。

代理模式就像明星会有经纪人一样,你要明星去开个演唱会或做点什么都要经过他的经纪人,经纪人说可以才可以,说不可以那你也没辙了。

var proxyFun = function(orig,ctrl){
    var Proxy= function(){
        var args = arguments,result,i;
        if(ctrl&& typeof ctrl== 'function'){
            //如果代理返回false, 则不继续执行
            if(before.apply(this,args)===false){
                return false;
            }
        }
        return orig.apply(this,args);
    }
    for(i in orig){
        if(orig.hasOwnProperty(i)){
            Proxy[i] = orig[i];
        }
    }
    Proxy.prototype = org.prototype;
    //保留原始函数对象, 以备复原
    Proxy.originalFunction = orig;
    return Proxy;
}

小结一下

上面都只是举些小例子,个人觉得在js中装饰器模式和代理模式是大同小异的,只是结构上基本一样,只是作用不同,核心都是方法劫持。这种模式的应用有很多,例如预处理消息、消息拦截、消息转发,消息善后处理等,并且我们可以基于这种模式实现AOP(Aspect Oriented Programming),面向切面编程。

基于这种模式,我们在开发阶段就可以专注于对象的核心功能,那些杂七杂八的事务管理、权限管理、日志记录啥的都可以在初期不用考虑,只需最后用AOP将它们切进去就行了,这是多么爽呀~

不过,要小心的是,这种方式带来的灵活性并不一定是好的。灵活性这东西就像氧气一样,太少氧气,生物则无法生存,太多氧气,丁点火星便可酿成燎原之灾。

 

发布者

Rolf

伪文艺IT攻城师,热爱前端,热爱互联。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

[喜欢] [嘻嘻] [奋斗] [问号] [鼓掌] [泪] [酷] [强] [耶] [握手] [心] [给力] [神马] [围观] [奥特曼] more »