俗话说,一个模式三个坑。 中介者模式应该算最坑的一个模式,坑不在于他的原理。而在于他的名字和其他模式的使用,真尼玛像。首先,“中介者“ 好像是一切模式里面都有的一个东西,比如,享元模式中-元对象,订阅发布模式中-全局监听Event... 但是,这个模式偏偏又叫做中介者模式(哎,曾经说模式的时候,感觉什么都是中介者模式)。
所以,这里我们首先要攻克的难关就是,中介者模式的features。
中介者模式
首先,我们需要回忆一下使用订阅发布模式中,是怎样一个场景
恩,中间需要有一个连接节点,即,发布者只需要和连接者有关联,而订阅者同样也只需要和连接者有关联。 所以,这个点就是中介者模式最独特的feature. 下图可以清楚的看出,中介者模式的特点。
知道了吧,中介者模式最突出的就是,由中介者来掌管一切,比订阅者中的连接节点的地位好像就是爸爸和儿子的关系。
中介者模式的实现
在发布订阅模式中有着全局对象Event的管理,那中介者模式中的boss应该怎样表达呢?
首先,我们需要说明一下,中介者模式主要的应用场景是什么。
有大量相互关联的对象
每个对象都能改变状态
你写的代码比较烂
差不多中介者模式能够解决以上的问题。
徒儿,给师傅去要个栗子来。
好的,师傅!!!
栗子来了:
大家好,(我假装我是一名学霸) 同学们可能经常去的地方,应该就是图书馆了,经常会去图书管理处借一些书来看,比如: 藏地密码,阿弥陀佛么么哒...等等。我们凭借着尽职的图书馆阿姨,往往可以借到我们想要的书本。如果已经被借走了,还可以在阿姨那里登记,并且如果书还回来的,会通知你过来取。
恩,总结一下:
图书馆阿姨其实就是一个中介者,我们找书,都是通过图书管理处询问,然后他们负责给我们查询。 如果没有这个管理处的话,就像我们平时一样,在群里问问,"大家有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~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。