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

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

每天一个JavaScript设计模式:简单工厂模式

一、适用场景

简单工厂模式是一种创建型模式,适用于创建同一类对象。
假如存在以下需求:

编写一系列的弹出框,包括:警告框(alert)、输入框(promt)、确认框(confirm)

我们自然可以编写Alert()Prompt()Confirm()三个类,然后每次调用时,使用new Alert()new Prompt()来调用。这在大多数情况下当然没有什么问题,但是假如有一天我们对警告框做了一些改进,然后把它命名为BetterAlert(),但此时项目里已经大量使用Alert()了,如此一来我们如果要应用BetterAlert(),就涉及很多地方的更改。如何解决这个问题?


二、核心思想

简单工厂模式的核心思想就是抽象出一个工厂函数,工厂函数内部通过传入的参数进行判断应该实例化和使用哪些类,使用者在使用时便只需要调用工厂函数即可,如此一来,即使更换了类名,我们也只需要修改工厂函数便可。
对于开头的场景,可以编写代码示例如下:

function popFactory(type, options) {
    switch (type) {
        case 'alert':
            return new Alert(options)
        case 'prompt':
            return new Prompt(options)
        case 'confirm':
            return new Confirm(options)
    }
}

如此,当我们要使用BetterAlert()替换Alert()时,只需修改return new Alert(options)return new BetterAlert(options)

除此之外,在场景中,我们会发现一个问题:

Alert、Prompt、Confirm之间会有很多共通点,如何抽象出这些共通点,使得代码更DRY呢?

我们便可使用另外一种方式:创建一个对象,然后对对象扩展属性和方法。这同样也是简单工厂模式,示例如下:

function popFactory(type, options) {
    const abstractPop = {}
    abstractPop.title = options.title || '标题'
    abstractPop.content = options.content || ''
    abstractPop.show = function() {
        // 显示弹框的处理逻辑
    }
    if (type === 'alert') {
        // 处理alert不同的逻辑
    }
    if (type === 'prompt') {
        // 处理prompt不同的逻辑
    }
    if (type === 'confirm') {
        // 处理confirm不同的逻辑
    }
    return abstractPop
}

所以在JavaScript中,简单工厂模式有两类创建对象的方法:
1)通过创建对象
2)通过创建一个新对象,然后包装增强其属性和方法
使用前者的好处在于,如果之间具有共用的方法,可以通过prototype来实现共用,而后者每次调用创建的都是新的方法,所以不能实现方法的共用。但是两者各有各的好处,具体方式则应该依据具体的业务需求。