面向对象设计鼓励把对象划分成更小的粒度,每个对象负责一个特定的功能,这有助于增强对象的可复用性。但由于这些细粒度对象之间的联系激增,又有可能会反过来降低它们的可复用性。当程序的规模增大,对象会越来越多,它们之间的关系也越来越复杂,难免会形成网状的交叉引用。如图
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。如图
假设我们正在编写一个手机购买的页面,在购买流程中,可以选择手机的颜色、内存大小以及输入购买数量,同时页面中有三个展示区域,分别向用户展示刚刚选择好的颜色、内存和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对应的库存,如果库存数量少于这次的购买数量,按钮将被禁用并且显示库存不足,反之按钮可以点击并且显示放入购物车
<!DOCTYPE html>
<html>
<body>
<div>
颜色: <select id="colorSelect">
<option value="">请选择</option>
<option value="red">红色</option>
<option value="blue">蓝色</option>
</select>
内存: <select id="memorySelect">
<option value="">请选择</option>
<option value="16G">16G</option>
<option value="32G">32G</option>
</select>
数量: <input type="number" id="numberInput" />
</div>
您选择了颜色: <div id="colorInfo"></div><br />
您选择了内存: <div id="memoryInfo"></div><br />
您输入了数量: <div id="numberInfo"></div><br />
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>
</body>
<script>
var goods = { // 手机库存
"red|32G": 3,
"red|16G": 0,
"blue|32G": 1,
"blue|16G": 6
};
// {"red|32G": 3, "red|16G": 0,"blue|32G": 1,"blue|16G": 6}
// [['red', 'blue'], ['32G', '16G']]
function getSku(obj) {
var keys = Object.keys(obj),
skuType = (keys.length && keys[0].split('|')) || [],
arr = [];
skuType.forEach(function(item, index){
arr.push([]); // 根据SKU类型,确认二维数组数量
keys.forEach(function(item) {
// 第一个数组存储颜色,第二数组存储内存,依次类推
var sku = item.split('|')[index]
if(arr[index].indexOf(sku) == -1) {
arr[index].push(sku)
}
})
})
return arr;
}
console.log(getSku(goods))
// 中介者
var mediator = (function() {
var colorSelect = document.getElementById('colorSelect'),
memorySelect = document.getElementById('memorySelect'),
numberInput = document.getElementById('numberInput'),
colorInfo = document.getElementById('colorInfo'),
memoryInfo = document.getElementById('memoryInfo'),
numberInfo = document.getElementById('numberInfo'),
nextBtn = document.getElementById('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 (!(/(^[1-9]d*$)/.test(number))) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
}
}
})();
// 事件函数
colorSelect.onchange = function() {
mediator.changed(this);
};
memorySelect.onchange = function() {
mediator.changed(this);
};
numberInput.oninput = function() {
mediator.changed(this);
};
</script>
</html>
使用中介者模式的优势很明显,颜色、内存和数量选择之间没有复杂的相互引用,所有的逻辑操作都由mediator对象集中管理。如果后期SKU又新增了CPU选择项,只需要稍微改动mediator对象
var goods = { // 手机库存
"red|32G|800": 3, // 颜色 red,内存 32G,cpu800,对应库存数量为 3
"red|16G|801": 0,
"blue|32G|800": 1,
"blue|16G|801": 6
};
var mediator = (function() {
// 略
var cpuSelect = document.getElementById('cpuSelect');
return {
change: function(obj) {
// 略
var cpu = cpuSelect.value,
stock = goods[color + '|' + memory + '|' + cpu];
if (obj === cpuSelect) {
cpuInfo.innerHTML = cpu;
}
// 略
}
}
})();