中介者模式
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系
中介者模式是迎合迪米特法则的一种实现。迪米特法则也叫最少知识原则,是指一个对象应该尽可能少地了解另外的对象(类似不和陌生人说话)。如果对象之间的耦合性太高,一个对象发生改变之后,难免会影响到其他的对象,在中介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方
例子
假如做一个魅族手机订购页面,我们可以选择手机颜色和购买数量,对应颜色的手机也有库存,现在客户可以选择颜色,输入数量,如果数量合理且不超过库存则可以购买,下方会显示选择的颜色和数量,可以购买时底部购买按钮才可以点击
1// ...2<div>3 <select id="colorSelector">4 <option value="">请选择</option>5 <option value="red">红色</option>6 <option value="blue">蓝色</option>7 </select>89 <input id="numberSelector" />10</div>1112<div>13 <span id="colorDisplay"></span>14 <span id="numberDisplay"></span>15</div>1617<button id="buy" type="button" disabled="true">购买</button>
1// data2const data = {3 red: 3,4 blue: 5,5}67const colorSelector = document.querySelector('#colorSelector')8const numberSelector = document.querySelector('#numberSelector')9const colorDisplay = document.querySelector('#colorDisplay')10const numberDisplay = document.querySelector('#numberDisplay')11const buy = document.querySelector('#buy')1213function canBuy(color, number) {14 if (number && (number <= 0 || number > data[color])) {15 return false16 }17 return true18}1920function numberDisplayContent(color, number) {21 if (!color) {22 return '请选择颜色'23 }24 if (number <= 0) {25 return '请输入大于零的数'26 }27 if (number > data[color]) {28 return '数量大于库存'29 }30 return number31}3233colorSelector.addEventListener('change', () => {34 colorDisplay.innerText = colorSelector.value35 numberDisplay.innerText = numberDisplayContent(numberSelector.value)36 if (canBuy(numberSelector.value)) {37 buy.disabled = false38 } else {39 buy.disabled = true40 }41})42numberSelector.addEventListener('input', () => {43 numberDisplay.innerText = numberDisplayContent(numberSelector.value)44 if (canBuy(numberSelector.value)) {45 buy.disabled = false46 } else {47 buy.disabled = true48 }49})
现在由于 colorSelector 和 numberSelector 在变化时都要判断 canBuy,colorDisplay 依赖于 numberSelector,没有解耦,如果要增加内存的选择则要修改很多
引入中介者:
1// data2const data = {3 'red&32': 3,4 'red&64': 5,5 'blue&32': 7,6 'blue&64': 2,7}89const colorSelector = document.querySelector('#colorSelector')10const numberSelector = document.querySelector('#numberSelector')11const memorySelector = document.querySelector('#memorySelector')12const colorDisplay = document.querySelector('#colorDisplay')13const numberDisplay = document.querySelector('#numberDisplay')14const memoryDisplay = document.querySelector('#memoryDisplay')15const buy = document.querySelector('#buy')1617function canBuy(key, number) {18 if (number && (number <= 0 || number > data[key])) {19 return false20 }21 return true22}2324const mediator = {25 changed(selector) {26 if (colorSelector.value) {27 colorDisplay.innerText = '请选择颜色'28 return29 }30 if (numberSelector.value) {31 numberDisplay.innerText = '请输入数量'32 return33 }34 if (memorySelector.value) {35 memoryDisplay.innerText = '请选择内存大小'36 return 37 }3839 const key = `${colorSelector.value}&${memorySelector.value}`4041 if (selector === colorSelector) {42 colorDisplay.innerText = selector.value43 } else if (selector === numberSelector) {44 numberDisplay.innerText = selector.value <= 045 ? '请输入大于零的数'46 : selector.value > data[key]47 ? '数量大于库存'48 : selector.value49 } else if (selector === memorySelector) {50 memoryDisplay.innerText = selector.value51 }5253 if (canBuy(key, selector.value)) {54 buy.disabled = false55 } else {56 buy.disabled = true57 }58 },59}6061colorSelector.addEventListener('change', function () {62 mediator.changed(this)63})64numberSelector.addEventListener('input', function () {65 mediator.changed(this)66})67memorySelector.addEventListener('change', function () {68 mediator.changed(this)69})
之后我们想要增加需求,只需改动中介者就行
优缺点
优点:
- 中介者模式使各个对象之间得以解耦,以中介者和对象之间的一对多关系取代了对象之间的网状多对多关系。各个对象只需关注自身功能的实现,对象之间的交互关系交给了中介者对象来实现和维护
缺点:
- 系统中会新增一个中介者对象,因为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者对象经常是巨大的。中介者对象自身往往就是一个难以维护的对象
中介者模式可以非常方便地对模块或者对象进行解耦,但对象之间并非一定需要解耦。在实际项目中,模块或对象之间有一些依赖关系是很正常的。毕竟我们写程序是为了快速完成项目交付生产,而不是堆砌模式和过度设计。关键就在于如何去衡量对象之间的耦合程度。一般来说,如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数增长曲线,那我们就可以考虑用中介者模式来重构代码