实用模式之中介者模式

俗话说,一个模式三个坑。 中介者模式应该算最坑的一个模式,坑不在于他的原理。而在于他的名字和其余模式的使用,真尼玛像。首先,“中介者“ 好像是一切模式里面都有的一个东西,好比,享元模式中-元对象,订阅发布模式中-全局监听Event... 可是,这个模式恰恰又叫作中介者模式(哎,曾经说模式的时候,感受什么都是中介者模式)。html

因此,这里咱们首先要攻克的难关就是,中介者模式的features。安全

中介者模式

首先,咱们须要回忆一下使用订阅发布模式中,是怎样一个场景app

恩,中间须要有一个链接节点,即,发布者只须要和链接者有关联,而订阅者一样也只须要和链接者有关联。 因此,这个点就是中介者模式最独特的feature. 下图能够清楚的看出,中介者模式的特色。函数

知道了吧,中介者模式最突出的就是,由中介者来掌管一切,比订阅者中的链接节点的地位好像就是爸爸和儿子的关系。工具

中介者模式的实现

在发布订阅模式中有着全局对象Event的管理,那中介者模式中的boss应该怎样表达呢?学习

首先,咱们须要说明一下,中介者模式主要的应用场景是什么。this

  1. 有大量相互关联的对象spa

  2. 每一个对象都能改变状态.net

  3. 你写的代码比较烂prototype

差很少中介者模式可以解决以上的问题。

徒儿,给师傅去要个栗子来。
好的,师傅!!!

栗子来了:

你们好,(我伪装我是一名学霸) 同窗们可能常常去的地方,应该就是图书馆了,常常会去图书管理处借一些书来看,好比: 藏地密码,阿弥陀佛么么哒...等等。咱们凭借着尽职的图书馆阿姨,每每能够借到咱们想要的书本。若是已经被借走了,还能够在阿姨那里登记,而且若是书还回来的,会通知你过来取。

恩,总结一下:

图书馆阿姨其实就是一个中介者,咱们找书,都是经过图书管理处询问,而后他们负责给咱们查询。 若是没有这个管理处的话,就像咱们平时同样,在群里问问,"你们有xxx书吗?能借我看两天吗?"。固然,这样每每会石沉大海。

咱们用代码模拟一下:

//咱们先假设图书管理系统只有借和还的功能
//我的
function Person(name){
    this.name = name;
}
Person.prototype.lend = function(bookName){
    Manager.assign('lend',this,bookName);
}
Person.prototype.back = function(bookName){
    Manager.assign('back',this,bookName);
}
//建立一个工厂模式
var peopleFactory = (function(){
    var people = {};
    return function(name){
        var person = people[name];
        if(person){
            return person;
        }
        person = new Person(name);
        people[name] = person;
        return person;
    }
})();
//中介者,图书管理处
var Manager = (function(){
    var lendList = {},
        stock = {},
        operations = {};
    operations.lend = function(person,bookName){
        var num = stock[bookName];

        if(num===undefined){  //判断是否有书
            console.log("图书馆没有该书");
            return;
        }
        if(num===0){  //书本已经借完
            console.log("该书已经借完");
            return;
        }
        stock[bookName]--;  //将数量减一
        lendList[person.name] = bookName;
        console.log('借阅成功');
    }
    operations.back = function(person,bookName){
        var bookName = lendList[person.name];  //返回借书人借的书名
        if(bookName === null){
            throw "该人,并无借书";
        }
        stock[bookName]++;  //还书
        lendList[person.name] = null;  //将借书人的清空
    }
    operations.addStock = function(){  //初始库存,addStock({bookName:jimmy,num:2})
        for(var i = 0,book;book = arguments[i++];){
            stock[book.bookName] = book.num;
        }
    }
    var assign = function(){
        var order = Array.prototype.shift.call(arguments);
        operations[order].apply(this,arguments);
    }
    return {
        assign
    }
})();
Manager.assign('addStock',{bookName:"藏地密码",num:1},{bookName:"阿弥陀佛么么哒",num:3});
var xiaoMing = peopleFactory("xiaoMing");
var jimmy = peopleFactory("jimmy");
var hanMM = peopleFactory("hanMM");
xiaoMing.lend("藏地密码");
jimmy.lend("藏地密码");
//还书的过程
xiaoMing.back("藏地秘密");
jimmy.lend('藏地密码');  //终于借成功了

以上是一个简单的中介者模式的缩影,要始终记着那张图表明的内涵,中介者模式是不须要在将请求传递出去的(或者说,情感上没有传递出去)。

中介者模式的现实意义

上面意淫了一个图书管理处(实话说,没有卵用). 咱们来个真的。

徒儿,给师傅找个栗子。
好,师傅!!!

咱们要学习帝吧人民,进能打td,退能刷淘宝。 咱们这里不说淘宝的事,但说一个电子商务的事。 如今Mooc这么火,各类线上课程也是numberous。我也上过。 一个线上的课程须要购买,购买的流程基本上就是,选择你想要的课程,而后,选择你要上课的人数(你是一个妈妈,你能够给你两个孩纸各买一个ID),若是上课人数未满的话,恭喜,你在家等开课就over了。若是课程满的话,你要么等下一期,要么,直接找另一门课。

若是使用,面向过程的思惟写的话,我相信这个,不是通常的复杂。

因此,咱们使用面向对象的思惟重构一下。

首先,咱们得拿到课程的数据,好比,课程名,已经报的课程人数,课程价格等。当咱们选择一个课程的时候,界面上确定须要做出相应的处理,好比,渲染课程价格,剩余人数。但咱们添加购买人数的时候,若是未超出,则能够购买,若是超出,则须要将购买的按钮改成不可选中状态。

恩,大体过程就是这样,咱们使用中介者模式想想。

首先,数据须要放在中介者模式内,用户的一切操做,都会传递给中介者模式,由他来选择是哪个html部分发生改变。

好,咱们用代码实现如下。

课程购买

上面是整个逻辑和页面,这里我主要针对js说明一下。

(function() {
    console.dir($(".courses"))
    bind($(".courses"), function(e) { //课程内容改变时
        mediator.command(e.target);
    }, 'change');
    bind($(".num"), function(e) { //报名人数改变时
        mediator.command(e.target);
    }, 'keyup');

    bind($(".buy"), function(e) { //绑定够买函数
        mediator.command(e.target);
    }, "click");

    var utils = (function() { //工具函数
        var change = function(ele, val) { //改变内容
            ele.innerHTML = val;
        }
        return {
            change
        };
    })();



    var mediator = (function() { //中介者
        var price = $(".price"), //课程价格
            remainder = $('.remainder'), //剩余人数
            num = $(".num"), //购买人数
            buyBtn = $('.buy'), //购买btn
            course;
        var changePR = function(courseName) { //改变价格和人数
            course = data[courseName];
            console.log(course);
            utils.change(price, course.price); //改变价格
            utils.change(remainder, course.remainder); //改变人数
        }
        var prohitBtn = (function() { //改变购买btn状态
            var use = `<button class="shoppingBtn">购买</button>`,
                ban = `<button class="prohit" disabled>人数已满</button>`,
                status = true;
            return function(flag) {
                if (status === flag) { //若是状态不变,则不改变内容
                    return;
                }
                if (flag === true) { //能够购买
                    buyBtn.innerHTML = use;
                    status = true;
                } else { //禁止购买
                    buyBtn.innerHTML = ban;
                    status = false;
                }
            }
        })();
        var detect = function() { //检测购买输入的内容
                var number = Number(num.value.trim());
                if (!course) {
                    alert("请先选择课程");
                    return;
                }
                if (number > course.remainder) {
                    prohitBtn(false); //不可以买
                }  else {
                    prohitBtn(true); //能够够买
                }
            }
            //当课程类改变时,触发改变名额,价格以及根据购买人数改变购买Btn的状态
        var coursesChange = function(courseName) {
            //改变价格和人数
            changePR(courseName);
            //根据input框的值,改变btn的状态
            detect();
        }
        var detectBuy = function() {
            var number = Number(num.value.trim());
            if (number === null || number == 0) {
                alert('请先输入购买数量~');
            } else {
                alert("购买成功");
            }
        }
        var command = function(target) {
            var classList = target.classList;
            if (classList.contains('courses')) {
                console.dir(target.value);
                var val = target.value;
                //执行
                coursesChange(val);
            } else if (classList.contains('num')) {
                var val =target.value.trim(),
                    reg = /\d+/;
                if (!reg.test(val)) {
                    alert("输入内容只能为数字");
                    return;
                }
                //执行
                detect();
            } else if(classList.contains("shoppingBtn")){
                detectBuy();
            }
        }
        return {
            command
        }
    })();
})();

能够从上面的代码看出,比较清晰的将沟壑关系解除,上面的中介者模式中也会用到代理模式等其余相关的知识。固然,这段代码并非特别好,关键在于处理的逻辑较多,有大量的if判断语句,因此也但愿读者,可使用之前所学的js模式进行重构,我相信到时候你的代码整洁程度必定远优于鄙人写的代码。

浅说中介者模式

其实,中介者模式是我最喜欢使用的模式之一,由于他好写易上手。可是缺点也是显而易见的,就是,你会在程序中增长一个巨大的对象,而你的噩梦就是维护这个对象。 中介者里面会包含大量的逻辑,设计较多的节点获取,形成的维护难度也是显而易见的。因此,仍是那句话,不要为了模式而模式,这个世界上没有万能的模式,就和没有绝对安全的系统同样。

ending~