Skip to content

设计模式-创建型模式

工厂模式

概念

  • 将创建对象的过程单独封装
  • 简单来说,就是你需要什么东西不直接使用new的方法生成实例,然后统一通过工厂进行生产加工再生成实例

示例1

需求

一个简单的成员管理系统,成员有姓名,成员类型,职责等基础属性,设一个生成各种角色实例的方法

实现

js
function User(name, age, type, works) {
    this.name = name
    this.age = age
    this.type = type
    this.works = works
}

function userFactory(name, age, type) {
    const worksObj = {
        'teacher': ['传道', '授业', '解惑'],
        'student': ['上课', '写作', '课外实践'],
        // ... any more
    }
    return new User(name, age, type, worksObj[type] || [])
}
const s1 = userFactory('小明',18,'student')
const t1 = userFactory('王刚',28,'teacher')

示例2

需求

图形库中有各各样的形状,如三角形,圆形,矩形等等,设计一个获取图形的方法

实现

js
function Circle(){
    this.name = 'circle'
    this.r = 0
}

function Triangle(){
    this.name = 'triangle'
    this.a = 0
    this.b = 0
    this.c = 0
}

function Rectangle(){
    this.name = 'rectangle'
    this.width = 0
    this.height = 0
}

function shapeFactory(shape){
    switch(shape){
        case 'circle':return new Circle()
        case 'triangle':return new Triangle()
        case 'rectangle':return new Rectangle()
        default: return null;
    }
}

const c1 = shapeFactory('circle')
const t1 = shapeFactory('triangle')
const r1 = shapeFactory('rectangle')

抽象工厂模式

概念

  • 围绕一个超级工厂创建其他工厂

示例1

需求

通常一个系统中除了成员管理,还包含日志管理等,不同系统中这些实例对象的属性可能不一致,但系统中都存在此类管理功能的需求,将系统的行为进行抽象。

实现

js
class AbstractSystemFactory {
    userFactory() {
        throw new Error('不允许直接调用抽象工厂方法')
    }

    logFactory() {
        throw new Error('不允许直接调用抽象工厂方法')
    }
}

class TradingSystem extends AbstractSystemFactory {
    userFactory() {
        return new TradingSystemUserFactory()
    }

    logFactory() {
        // 类似实现
    }
}

class AbstractSystemUserFactory {
    createUser() {
        throw new Error('不允许直接调用抽象工厂方法')
    }
}

class TradingSystemUserFactory extends AbstractSystemUserFactory {
    createUser(name, age) {
        return new TradingSystemUser(name, age)
    }
}

class TradingSystemUser {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    printInfo() {
        console.log(this.name, this.age);
    }
}

const tradingSystem = new TradingSystem()
const tradingSystemUserFactory = tradingSystem.userFactory()

const tsu1 = tradingSystemUserFactory.createUser('xm',18)
tsu1.printInfo()

单例模式

概念

  • 一个实例只生产一次

多种实现方式

使用ES5(Function)与ES6(Class)实现

方式1

利用instanceof判断是否使用new关键字调用函数进行对象的实例化

js
function User() {
    if (!(this instanceof User)) {
        return
    }
    if (!User._instance) {
        this.name = '无名'
        User._instance = this
    }
    return User._instance
}

const u1 = new User()
const u2 = new User()

console.log(u1===u2);// true

方式2

在函数上直接添加方法属性调用生成实例

js
function User(){
    this.name = '无名'
}
User.getInstance = function(){
    if(!User._instance){
        User._instance = new User()
    }
    return User._instance
}

const u1 = User.getInstance()
const u2 = User.getInstance()

console.log(u1===u2);

方式3

使用闭包,改进方式2

js
function User() {
    this.name = '无名'
}
User.getInstance = (function () {
    var instance
    return function () {
        if (!instance) {
            instance = new User()
        }
        return instance
    }
})()

const u1 = User.getInstance()
const u2 = User.getInstance()

console.log(u1 === u2);

方式4

使用包装对象结合闭包的形式实现

js
const User = (function () {
    function _user() {
        this.name = 'xm'
    }
    return function () {
        if (!_user.instance) {
            _user.instance = new _user()
        }
        return _user.instance
    }
})()

const u1 = new User()
const u2 = new User()

console.log(u1 === u2); // true

当然这里可以将闭包部分的代码单独封装为一个函数

在频繁使用到单例的情况下,推荐使用类似此方法的方案

js
function SingleWrapper(cons) {
    // 排出非函数与箭头函数
    if (!(cons instanceof Function) || !cons.prototype) {
        throw new Error('不是合法的构造函数')
    }
    var instance
    return function () {
        if (!instance) {
            instance = new cons()
        }
        return instance
    }
}

function User(){
    this.name = 'xm'
}
const SingleUser = SingleWrapper(User)
const u1 = new SingleUser()
const u2 = new SingleUser()
console.log(u1 === u2);

方式5

在构造函数中利用new.target判断是否使用new关键字

js
class User{
    constructor(){
        if(new.target !== User){
            return
        }
        if(!User._instance){
            this.name = 'xm'
            User._instance = this
        }
        return User._instance
    }
}

const u1 = new User()
const u2 = new User()
console.log(u1 === u2);

方式6

使用static静态方法

js
class User {
    constructor() {
        this.name = 'xm'
    }
    static getInstance() {
        if (!User._instance) {
            User._instance = new User()
        }
        return User._instance
    }
}


const u1 = User.getInstance()
const u2 = User.getInstance()

console.log(u1 === u2);

建造者模式

概念

  • 让简单的对象通过组合的方式构造成多种复杂对象
  • 一种创建复杂对象的最佳实践

示例

以团购火锅卷为例,通常有多种套餐类型可供选择,不同套餐由饮料,小吃,荤菜,素菜等等构成。饮料用瓶装,小吃用木碗装等等。

js
// 木盘子
class WoodenBowl {
    pack() {
        return 'WoodenBowl'
    }
}

// 瓶子
class Bottle {
    pack() {
        return 'Bottle'
    }
}

// 小吃用木碗装
class Snack {
    packing() {
        return new WoodenBowl()
    }
}

// 饮品用瓶装
class Drink {
    packing() {
        return new Bottle()
    }
}

// 可乐
class Coke extends Drink {
    price() {
        return 3.00
    }
    name() {
        return 'Coke'
    }
}

// 茶
class Tea extends Drink {
    price() {
        return 5.00
    }
    name() {
        return 'Tea'
    }
}

// 薯条
class FrenchFries extends Snack {
    price() {
        return 15.00
    }
    name() {
        return 'FrenchFries'
    }
}

// 面包
class Bread extends Snack {
    price() {
        return 5.00
    }
    name() {
        return 'Bread'
    }
}

// 套餐
class Meal {
    constructor() {
        this.items = []
    }
    addItem(item) {
        this.items.push(item);
    }
    getCost() {
        let cost = 0.0;
        for (const item of this.items) {
            cost += item.price();
        }
        return cost;
    }
    showItems() {
        for (const item of this.items) {
            const nameStr = "Item : " + item.name();
            const packStr = "Packing : " + item.packing().pack();
            const priceStr = "Price : " + item.price();
            console.log(`${nameStr},${packStr},${priceStr}`);
        }
    }
}

//  建造套餐
class MealBuilder {
    prepare2People() {
        const meal = new Meal();
        meal.addItem(new Coke());
        meal.addItem(new Tea());
        meal.addItem(new Bread());
        meal.addItem(new FrenchFries());
        return meal;
    }
    prepare1People() {
        const meal = new Meal();
        meal.addItem(new Coke());
        meal.addItem(new FrenchFries());
        return meal;
    }
}

const mealBuilder = new MealBuilder();
const people2 = mealBuilder.prepare2People();
people2.showItems();
console.log("Total Cost: " + people2.getCost());

输出

Item : Coke,Packing : Bottle,Price : 3
Item : Tea,Packing : Bottle,Price : 5
Item : Bread,Packing : WoodenBowl,Price : 5
Item : FrenchFries,Packing : WoodenBowl,Price : 15
Total Cost: 28

原型模式

概念

  • 原型模式是一种创建对象的方式
  • 利用实例来描述对象,用实例作为定义对象和继承的基础
  • 用原型模式的优势是使用更小的代价来创建对象,通过原型引用的方式而不是开辟新的空间

JS创建对象的方式就是原型引用: