愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

JavaScript设计模式——中介者模式

中介者模式的核心在于接触多个对象之间错综复杂的关系,使得每个对象只需要和中介者进行通信,而对象与其他对象之间的联系则交由中介者传达。

一、场景

现在有场景,某购物网站的商品详情页中的添加到购物车模块,需要包含有如下的逻辑
1)选择商品规格,然后填写购买数量
2)如果购买数量大于可售数量,则加入购物车按钮置灰
3)如果没有填写购买数量,则按钮提示请填写数量
实现如下:

选择颜色:
<select id="colorSelect">
    <option value="red">红色</option>
    <option value="blue">蓝色</option>
</select>
输入数量:
<input type="text" id="numberInput">
当前选择了:<span id="colorInfo"></span><span id="numberInfo"></span>
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量</button>

js代码如:

const colorSelect = $('#colorSelect'),
      numberInput = $('#numberInput'),
      colorInfo = $('#colorInfo'),
      numberInfo = $('#numberInfo'),
      nextBtn = $('#nextBtn')
const goods = {
    red: 3,
    blue: 6
}
colorSelect.onchange = function() {
    let color = this.value,
        number = numberInput.value,
        stock = goods[color]

    colorInfo.innerHTML = color 
    if (!color) {
        nextBtn.disabled = true
        nextBtn.innerHTML = '请选择手机颜色'
        return
    } 
    if ( ((number - 0)|0) !== number - 0 ) {
        nextBtn.disabled = true
        nextBtn.innerHTML = '请输入正确的购买数量'
        return
    }
    if (number > stock) {
        nextBtn.disabled = true
        nextBtn.innerHTMl = '库存不足'
        return
    }
    nextBtn.disable = false
    nextBtn.innerHTML = '加入购物车'
}

而此时,除了选择颜色的select变更时需要对各个状态做出响应之外,在数量改变的时候也需要做出响应,如:

numberInput.oninput = function() {
    let color = colorSelect.value,
        number = this.value,
        stock = goods[color]

    numberInfo.innerHTML = number 
    if (!color) {
        nextBtn.disabled = true
        nextBtn.innerHTML = '请选择手机颜色'
        return
    } 
    if ( ((number - 0)|0) !== number - 0 ) {
        nextBtn.disabled = true
        nextBtn.innerHTML = '请输入正确的购买数量'
        return
    }
    if (number > stock) {
        nextBtn.disabled = true
        nextBtn.innerHTMl = '库存不足'
        return
    }
    nextBtn.disable = false
    nextBtn.innerHTML = '加入购物车'
}


二、难题

在上例中,我们可以发现,当select和input的输入值都发生了变化的时候,需要重复处理一样的逻辑,而麻烦的不仅仅是这里。我们可以画出模块间的耦合关系图:

可以看到,仅仅是这么一个简单的逻辑下,就已经产生了如此错综复杂的联系。这样子一来,耦合度高,很容易造成代码的难以维护。
而中介者模式,则是为了解决这种问题而产生的。中介者对象作为所有对象的枢纽中心,它接收来自其他对象的通知,并管理和其他对象的关系,如此一来,可以实现其他对象之间的解耦。引入中介者对象的关系图如:

由于实现了这种方式的解耦,我们此后就需要维护中介者对象即可。其他对象彼此之间都不关心是否存在彼此,它们只需要跟中介者打交道即可,而它们的联系,则都由中介者对象处理。


三、解决问题

所以,现在我们可以引入中介者对象来解决开头的问题了:

const mediator = (function() {
    const changed = function(obj) {
        let color = colorSelect.value,
            number = numberInput.value
        if (obj === colorSelect) {
            colorInfo.innerHTML = color
        } else if (obj === numberInput) {
            numberInfo.innerHTML = number
        }

        if (!color) {
            nextBtn.disabled = true
            nextBtn.innerHTML = '请选择手机颜色'
            return
        } 
        if ( ((number - 0)|0) !== number - 0 ) {
            nextBtn.disabled = true
            nextBtn.innerHTML = '请输入正确的购买数量'
            return
        }
        if (number > stock) {
            nextBtn.disabled = true
            nextBtn.innerHTMl = '库存不足'
            return
        }
        nextBtn.disable = false
        nextBtn.innerHTML = '加入购物车'
    }
    return { changed }
})()

colorSelect.onchange = function() {
    mediator.changed(this)
}
numberInput.onchange = function() {
    mediator.changed(this)
}

如此一来,当我们有一天需要新增新的select,也就仅仅需要在中介者对象里加入少量的代码,并且指定一个新的onchange函数,就可以完成需求了。好处是明显的


四、总结

1)中介者模式解决了多个对象之间彼此紧密耦合的问题,通过引入中介者,多个对象之间能够实现不关心彼此。而只需要考虑和中介者对象通信即可,大大降低了多个对象之间的耦合。
2)但是缺点也是有的,首先中介者对象本身可能就是一个很复杂的对象,多个对象之间耦合的复杂性虽然通过中介者对象解耦了,但是反过来增加的则是中介者对象的复杂性。此外,还造成了系统中需要多引入一个中介者对象的问题。
3)中介者模式仍然是具有很大的价值的,是否采用中介者模式,主要取决于我们对对象之间耦合程度的要求