前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TS 设计模式01 - 工厂模式

TS 设计模式01 - 工厂模式

作者头像
love丁酥酥
修改2020-09-26 13:59:15
7720
修改2020-09-26 13:59:15
举报
文章被收录于专栏:coding for lovecoding for love

1. 简介

工厂,在现实中是生产产品的地方。在 oop 中,就是生产对象的地方。其核心是封装一个生产(new)行为。

2. 简单工厂

假如现在你运营了一间餐厅,客人如果要喝饮料,自己制作一杯就好,比如自己制作一杯茶或者一杯果汁。你觉得方便,顾客自己动手,丰衣足食,给你省了很大成本。顾客也觉得方便,想喝的时候自己就可以做,不用麻烦任何人,也不用等待。可是问题来了,顾客原本制作茶(new Tea())时我们是提供的本地茶叶冲泡的茶,但是成本太高,于是想换为绿茶茶包来制作(new GreenTea())。你需要通知所有泡茶的顾客更改他们的制作方式,这样的话,成本是不是太大了呢? 假设我提供一个饮品机器,能够为我们生产茶和果汁,用户想要什么饮品,按一下对应的按钮就可以。 我们来设计一下 UML 类图:

image.png

这里饮品我们用的接口,当然也可以用一个抽象类。下面是代码实现:

代码语言:javascript
复制
interface IDrink {
    name: string; // 饮品名称
    make(): void; // 制作饮品
    show(): void; // 展示饮品
}

// 茶
class Tea implements IDrink {
    name: string;
    constructor(name) {
        this.name = name;
        this.make();
    }
    make(): void {
        console.log('make tea');
    }
    show(): void {
        console.log(`this is ${this.name}`)
    }
}

// 果汁
class Juice implements IDrink {
    name: string;
    constructor(name) {
        this.name = name;
        this.make();
    }
    make(): void {
        console.log('make juice');
    }
    show(): void {
        console.log(`this is ${this.name}`)
    }
}

class SimpleDrinkFactory {
    static createDrink(type: string):: Tea | Juice  {
        switch (type) {
            case 'tea':
                return new Tea('tea');
            case 'juice':
                return new Juice('juice');
            default:
                return new Juice('juice');
        }
    }
}

// 告诉简单工厂想要一杯茶
const tea: Tea = SimpleDrinkFactory.createDrink('tea');
tea.show();
// 告诉简单工厂想要一杯果汁
const juice: Juice = SimpleDrinkFactory.createDrink('juice');
juice.show();

image.png

如果我们要把茶换成绿茶,只需新增一个绿茶类,并且在工厂中对茶的生产做一个修改即可,而不需要修改原来调用工厂生产茶的逻辑。

代码语言:javascript
复制
class GreenTea extends Tea {
    make(): void {
        console.log('make green tea');
    }
}

class SimpleDrinkFactory {
    static createDrink(type: string): : Tea | Juice  {
        switch (type) {
            case 'tea':
                return new GreenTea('green tea');
            case 'juice':
                return new Juice('juice');
            default:
               return new Juice('juice');
        }
    }
}

image.png

这里我们对产品的生产做了一层封装,隔离了用户和产品的直接关系,用户只需要和工厂打交道即可。这里注意以下几点:

  1. 理论上,简单工厂可以生成任意不相关的对象(返回 any),实际上我们还是会把具有相同特征(IDrink)的产品放在一块。
  2. 简单工厂的灵活性不强,不支持传入不同的参数。比如调用工厂时传入不同的参数,让其在生成产品时去调用,这种在 ts 是很难做到的,但你要说在 js,那就很 easy 了。
  3. 扩展很麻烦,比如你要新增一种饮品类型,这里就得修改工厂,改造你的饮品机器,加一个按键,违反了开闭原则。 其实简单工厂不属于设计模式的一种,但他的这种思想还是很强大的,运用也很广泛,为我们提供了一种最简单的封装创建行为的方式。

3. 工厂模式

可以看到,简单工厂扩展时需要修改工厂。这是因为再简单工厂内部我们用到了所有具体的产品,所以我们应该继续抽象,让我们的创建行为依赖一个抽象的工厂,而具体的创建行为由一个具体的工厂子类来实现。

image.png

代码语言:javascript
复制
interface IDrinkFactory {
    createDrink(): IDrink;
}

class TeaFactory implements IDrinkFactory {
    createDrink(): Tea {
        return new Tea('tea');
    }
}

class JuiceFactory implements IDrinkFactory {
    createDrink(): Juice {
        return new Juice('juice');
    }
}

// 告诉工厂想要一杯茶
const tea = new TeaFactory().createDrink();
tea.show();
// 告诉工厂想要一杯果汁
const juice = new JuiceFactory().createDrink();
juice.show();

这就好比新增一种饮品,我在新增一台机器即可,符合开闭原则。但是如果我的饮品种类很多,我就要增加很多机器,假设我还有生产食物的需求,我也得为食物生产定制很多的机器。那么我的管理成本会越来越大。

4. 抽象工厂模式

其实顾客消费时,既有可能消费饮品,也有可能消费食物,如果我们的抽象工厂同时提供这两种产品的创建,那么我们所需要实现的具体工厂就会少很多。也就是我们的工厂支持不同的产品系创建。

image.png

代码语言:javascript
复制
interface IDrink {
    name: string; // 饮品名称
    make(): void; // 制作饮品
    show(): void; // 展示饮品
}

// 茶
class Tea implements IDrink {
    name: string;

    constructor(name) {
        this.name = name;
        this.make();
    }

    make(): void {
        console.log('make tea');
    }

    show(): void {
        console.log(`this is ${this.name}`);
    }
}

// 果汁
class Juice implements IDrink {
    name: string;

    constructor(name) {
        this.name = name;
        this.make();
    }

    make(): void {
        console.log('make juice');
    }

    show(): void {
        console.log(`this is ${this.name}`);
    }
}

interface IFood {
    name: string;
    make(): void; // 制作食品
    show(): void; // 展示食品
}

// 炸鸡
class FriedChicken implements IFood {
    name: string;

    constructor(name) {
        this.name = name;
        this.make();
    }

    make(): void {
        console.log('make FriedChicken');
    }

    show(): void {
        console.log(`this is ${this.name}`);
    }
}

// 汉堡
class Hamburg implements IFood {
    name: string;

    constructor(name) {
        this.name = name;
        this.make();
    }

    make(): void {
        console.log('make hamburg');
    }

    show(): void {
        console.log(`this is ${this.name}`);
    }
}

interface IMealFactory {
    createDrink(): IDrink;
    createFood(): IFood;
}

class MealAFactory implements IMealFactory {
    createDrink(): Tea {
        return new Tea('tea');
    }

    createFood(): FriedChicken {
        return new FriedChicken('fried chicken');
    }
}

class MealBFactory implements IMealFactory {
    createDrink(): Juice {
        return new Juice('juice');
    }

    createFood(): Hamburg {
        return new Hamburg('hamburg');
    }
}

const mealAFactory = new MealAFactory();
mealAFactory.createDrink().show();
mealAFactory.createFood().show();
const mealBFactory = new MealBFactory();
mealBFactory.createDrink().show();
mealBFactory.createFood().show();

抽象工厂解决了创建不同产品簇的问题,不过抽象工厂在扩展时仍然会产生大量的实体工。

5. 小结

从工厂模式的三种形态中,我们可以看到。越抽象就越利于扩展,但同时也增大了代码结构的复杂性和管理难度,另外,越抽象,有时候就意味着约束力更弱。比如这里具体工厂生产的产品类型,我们使用的具体产品类型来约束,而不是抽象的产品接口来约束,虽然第二种方法扩展和修改起来更方便,但是代码的约束力更弱,可能会写出不符合预期的代码。 没有完美的设计模式,只有合适的设计方案,不要在一开始就追求过度设计。我们只要理解工厂模式的核心就在于封装 new 这个行为。

参考

[book - 大话设计模式] [book - 设计模式之禅] 工厂模式_百度百科 工厂模式 | 菜鸟教程 设计模式之工厂模式(factory pattern) 简单工厂模式 设计模式之工厂模式(Factory Pattern) 从ES6重新认识JavaScript设计模式(二): 工厂模式 TypeScript实现设计模式——工厂模式 工厂方法模式(Factory Method)-最易懂的设计模式解析 简单工厂模式(SimpleFactoryPattern)- 最易懂的设计模式解析 java的三种工厂模式 java 三种工厂模式

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简介
  • 2. 简单工厂
  • 3. 工厂模式
  • 4. 抽象工厂模式
  • 5. 小结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档