zoukankan      html  css  js  c++  java
  • 轻松掌握: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设计模式与开发实践》

  • 相关阅读:
    Sql语句创建表
    刷新、关闭等误操作造成当前页面信息的丢失的解决方案
    asp.net文本框中只允许用户输入数字
    为每个用户创建文件夹,并实现图片上传
    在updatepanel中使用fileupload控件
    解决弹出提示框后字体变大的BUG
    SQL 2005新增的几个函数之学习
    .net实现单张图片的上传
    数据分页
    所谓三门问题
  • 原文地址:https://www.cnblogs.com/susufufu/p/5808433.html
Copyright © 2011-2022 走看看