JavaScript 设计模式(二)— 创建型设计模式

47 阅读3分钟

创建型设计模式是一类处理对象创建的设计模式,通过控制对象的创建来避免基本对象创建时可能导致设计上的问题或者增加设计上的难度,能够提升已有代码的灵活性和可复用性。

工厂方法模式

通过对产品类的抽象使其创建业务主要用于创建多类产品的实例。
在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
比较常见的应该是项目中对 api 请求的封装吧🤔。

// 某物流中心包含陆运、航运、空运,根据运输方式选择交通工具运送产品
let transportFactory = function (type) {
    if (this instanceof transportFactory) {
        let transport = new this[type]();
        return transport;
    } else {
        return new transportFactory(type);
    }
};
transportFactory.prototype = {
    landTransportation: function () {
        this.getUtil = () => {
            return 'car';
        };
    },
    shipping: function () {
        this.getUtil = () => {
            return 'boat';
        };
    },
    airTransport: function () {
        this.getUtil = () => {
            return 'aircraft';
        };
    },
};
let sea = transportFactory('shipping');
sea.getUtil(); // 'boat

抽象工厂模式

通过对类的工厂抽象,使其业务用于对产品类簇的创建,而不负责创建某一产品。
类簇(class cluster),顾名思义,即一坨类,这里指的是继承自同一父类的一组私有子类。
父类定义了该类必要的抽象方法,若子类未重写则不可用。

// 汽车抽象工厂
let VehicleFactory = function (subType, superType) {
    if (typeof VehicleFactory[superType] === 'function') {
        // 缓存类,参考寄生式继承
        function Fn() {}
        Fn.prototype = new VehicleFactory[superType]();
        subType.prototype = new Fn();
        subType.constructor = subType;
    } else {
        throw new Error('未创建该抽象类');
    }
};
// 小汽车抽象类
let Car = function () {
    this.type = 'Car';
};
Car.prototype = {
    getPrice: function () {
        return new Error('抽象方法不可用');
    },
};
// 宝马类
let BMW = function (name) {
    this.name = name ? name : '乌骓';
    this.getPrice = function () {
        return `${this.name} is 666`;
    };
};
VehicleFactory.Car = Car;
// 抽象工厂实现对Car抽象类的继承
VehicleFactory(BMW, 'Car');
let bmw = new BMW('赤兔');
bmw.getPrice(); // 赤兔 is 666

建造者模式

将一个复杂对象的构建层与其表现层相互分离,同样的构建过程可采用不同表示。
相较于工厂模式,建造者模式更关心创建对象的整个过程,甚至对象创建的每一细节,将创建对象的类模块化。

// 某公司处理招聘者信息,包括应聘者类、职位类、用户信息类
let Work = function () {};
let User = function () {};
let Human = function () {};
// 应聘者(包含多个模块)
let Person = function () {
    let _person = new Human();
    _person.work = new Work();
    _person.user = new User();
    return _person;
};
let person = new Person();

原型模式

原型对象指向创建对象的类,使用于创建新对象的类共享原型对象的属性和方法。
参考组合继承或寄生式组合继承,子类的原型指向父类对象,使新创建的子类对象能够共享父类原型的方法和属性,减少实例化时的开销。
如果涉及多个对象,可以考虑通过对对象属性和方法进行复制来实现创建。

// 浅拷贝模版对象属性
function prototypeExtend() {
    let Fn = function () {},
        args = arguments,
        i = 0,
        len = args.length;
    for (; i < len; i++) {
        // 遍历每个模板对象的属性
        for (const j in args[j]) {
            // 复制到缓存类型中
            F.prototype[j] = args[i][j];
        }
    }
    return new F();
}

单例模式

又称单体模式,只允许实例化一次的对象类。
有时可以使用一个对象来规划一个命名空间,管理对象上的属性和方法。
例如早期的 jQuery 框架,通过 jquery 对象管理各个功能模块。
单例模式更适合管理静态变量。

let Conf = (function () {
    // 静态变量大写是一种定义习惯,其他编程语言也是如此
    let conf = {
        MAX_SIZE: 100,
        MIN_SIZE: 1,
        COUNT: 10,
    }
    // 取值器
    return {
        get: function (name) {
            return conf[name] ? conf[name] : null;
        }
    };
})(),

有时需要对单例对象延迟创建,成为惰性单例

let lazySingle = function () {
    let _instance = null;
    function single() {
        return {
            publishMethod: function () {},
            publishPrototype: '1.0',
        };
    }
    return function () {
        if (!_instance) {
            _instance = single();
        }
        // 返回单例
        return _instance;
    };
};