轻松掌握:JavaScript代理模式、中介者模式

代理模式、中介者模式

代理模式

在面向对象设计中,有一个单一职责原则,指就一个类(对象、函数)而言,应该仅有一个引起它变化的原因。如果一个对象承担了过多的职责,就意味着它将变得巨大,引起它变化的原因就多,它把这些职责耦合到了一起,这种耦合会导致程序难于维护和重构。

这时候,我们可以把该对象(本体)的其中一部分职责分离出来给一些第三方对象去做,本体只管自己的一些核心职责,这些第三方对象就称作代理。代理对象可以作为对象(也叫“真正的主体”)的保护者,让真正的主体对象做尽量少的工作。在代理设计模式中,一个对象充当了另一个对象的接口的角色。

通常代理和本体的接口应该保持一致性,这样当不需要代理的时候,用户可直接访问本体。

当我们不方便直接访问一个对象时,就可以考虑给该对象招一个代理。

代理可用于:图片预加载、合并HTTP请求(代理收集一定时间内的所有HTTP请求,然后一次性发给服务器)、惰性加载(通过代理处理和收集一些基本操作,然后仅在真正需要本体的时候才加载本体)、缓存代理(缓存请求结果、计算结果)等

例子1:图片预加载

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc:function(src){
            imgNode.src = src;
        }
    }
})();
//代理函数
var proxyImage = (function(){
    var img = new Image;
    img.onload = function(){
        myImage.setSrc(this.src);
    }
    return{
        setSrc:function(src){
            myImage.setSrc('loading.gif');
            img.src = src;
        }
    }
})();

proxyImage.setSrc('show.jpg');

中介者模式

中介者模式的作用就是解除对象与对象之间的紧耦合关系,它也称‘调停者’。所有的对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者即可。

如:机场的指挥塔,每架飞机都只需要和指挥塔通信即可,指挥塔知道每架飞机的飞行状况,可以安排所有起降时间,调整航线等

中介者模式符合迪米特法则,即最少知识原则,指一个对象应该尽可能少地了解另外的对象。如果对象之间的耦合性太高,则改变一个对象,会牵动很多对象,难于维护。当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的。

如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长,那我们就可以考虑用中介者模式来重构代码!中介者通过解耦来提升代码的可维护性。

例子1:游戏

玩家对象是通过Player()构造函数来创建的,有自己的points和name属性。原型上的play()方法负责给自己加一分然后通知中介者:

function Player(name) {
    this.points = 0;
    this.name = name;
}
Player.prototype.play = function () {
    this.points += 1;
    mediator.played();
};

scoreboard对象(计分板)有一个update()方法,它会在每次玩家玩完后被中介者调用。计分析根本不知道玩家的任何信息,也不保存分数,它只负责显示中介者给过来的分数:

var scoreboard = {
    element: document.getElementById('results'),
    update: function (score) {
        var i, msg = '';
        for (i in score) {
            if (score.hasOwnProperty(i)) {
                msg += '<p><strong>' + i + '<\/strong>: ';
                msg += score[i];
                msg += '<\/p>';
            }
        }
        this.element.innerHTML = msg;
    }
};

现在我们来看一下mediator对象(中介者)。在游戏初始化的时候,在setup()方法中创建游戏者,然后放后players属性以便后续使用。played()方法会被游戏者在每轮玩完后调用,它更新score哈希然表然后将它传给scoreboard用于显示。最后一个方法是keypress(),负责处理键盘事件,决定是哪位玩家玩的,并且通知它:

var mediator = {
    players: {},
    setup: function () {
        var players = this.players;
        players.home = new Player('Home');
        players.guest = new Player('Guest');
    },
    played: function () {
        var players = this.players,
        score = {
            Home: players.home.points,
            Guest: players.guest.points
        };
        scoreboard.update(score);
    },
    keypress: function (e) {
        e = e || window.event; // IE
        if (e.which === 49) { // key "1"
            mediator.players.home.play();
            return;
        }
        if (e.which === 48) { // key "0"
            mediator.players.guest.play();
            return;
        }
    }
};

最后一件事是初始化和结束游戏:

// go!
mediator.setup();
window.onkeypress = mediator.keypress;

// game over in 30 seconds
setTimeout(function () {
    window.onkeypress = null;
    alert('Game over!');
}, 30000);

例子2:卖手机

var goods = {   //库存
    'red|32G':3,
    'red|16G':5,
    'blue|32G':3,
    'blue|16G':6
}
//中介者
var mediator = (function(){
    function id(id){
        return document.getElementById(id);
    }
    var colorSelect = id('colorSelect'),
        memorySelect = id('memorySelect'),
        numberInput = id('numberInput'),
        colorInfo = id('colorInfo'),
        memoryInfo = id('memoryInfo'),
        numberInfo = id('numberInfo'),
        nextBtn = id('nextBtn');
    return{
        changed:function(obj){
            var color = colorSelect.value,
                memory = memorySelect.value,
                number = numberInput.value,
                stock = goods[color+'|'+memory];
            if(obj === colorSelect){
                colorInfo.innerHTML = color;
            }else if(obj === memorySelect){
                memoryInfo.innerHTML = memory;
            }else if(obj === numberInput){
                numberInfo.innerHTML = number;
            }
            if(!color){
                nextBtn.disabled = true;
                nextBtn.innerHTML = '请选择手机颜色';
                return;
            }
            if(!memory){
                nextBtn.disabled = true;
                nextBtn.innerHTML = '请选择内存大小';
                return;
            }
            if(Number.isInteger(number-0) && number > 0){
                nextBtn.disabled = true;
                nextBtn.innerHTML = '请输入正确的购买数量';
                return;
            }
            nextBtn.disabled = false;
            nextBtn.innerHTML = '放入购物车';
        }
    }
})();
//添加事件
colorSelect.onchange = function(){
    mediator.changed(this);
}
memorySelect.onchange = function(){
    mediator.changed(this);
}
numberInput.onchange = function(){
    mediator.changed(this);
}

参考文献: 《JavaScript模式》 《JavaScript设计模式与开发实践》

posted on 2016-08-25 21:30  SuriFuture  阅读(1362)  评论(0编辑  收藏  举报

导航