Z

工厂与抽象工厂模式(Factory & Abstract Factory Pattern)

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.

工厂

工厂模式如其名,是一个类生产工厂,用于实例化类。工厂模式提供一个工厂类,其子类负责实例化不同的类,可以简单的用ts实现:

 1abstract class MobilePhone {
 2    abstract call(): void;
 3}
 4
 5class iPhoneCls implements MobilePhone {
 6    call() {
 7        console.log("Apple!");
 8    }
 9}
10
11class SonyCls implements MobilePhone {
12    call() {
13        console.log("Sony!");
14    }
15}
16
17
18class MobilePhoneFactory {
19    createPhone(vendor: "Apple" | "Sony"): MobilePhone {
20        switch (vendor) {
21            case "Apple":
22                return new iPhoneCls();
23            case "Sony":
24                return new SonyCls();
25        }
26    }
27}
28
29const factory = new MobilePhoneFactory();
30
31const iPhone = factory.createPhone("Apple");
32const Sony = factory.createPhone("Sony");
33
34iPhone.call();
35Sony.call();

在上面的代码中,有一个抽象类 MobilePhone,它描述了子类的方法,在实际定义子类时,需要implements这个抽象类。MobilePhoneFactory 是工厂类,它不负责实例化类,而是依靠其子类方法 createPhone 根据参数进行实例化。

使用工厂模式的意义何在?或许你会觉得,我需要iPhone的地方直接new iPhone(),需要Sony的地方直接 new Sony()不就行了,专门写个类来负责实例化,不是脱裤子放屁么?

那么考虑一下场景:你带代码有上万行的规模,里面进行了无数次的new操作,还夹杂了还有new出来实例被赋值给别的变量,这时候你写了个新类,想替换老的类,怎么样?想想是不是就头疼?更进一步,新类包含了新特性,需要上线进行A/B Test,咋整?难道把这无数次new操作全套上if-else?

工厂模式的意义就在于此,它完全“屏蔽”掉了产品类,只提供一个类工厂,消费者无需关注我到底要new哪个类,我只要传正确的约束参数,就能得到想要的类,上面的场景只需要在类工厂里进行就行了,消费者完全无感知。

在实际开发过程中,可能并不需要创建一个类工厂并使用其子类来实例化类,或许使用静态方法或者函数就行,此时工厂模式缩小为“简单工厂”模式,这也是实际开发中最常用的。

抽象工厂

Apple和Microsoft分别有自己的手机和笔记本品牌,那我们能不能通过类似工厂模式的结构管理这两种厂商各自的产品呢?这就需要将工厂模式升级至抽象工厂。

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

当有多种产品或业务分类时,使用抽象工厂生产对象是一个很好的方式。通常抽象工厂模式会有这些角色:

  • 不同产品的抽象类(用来描述抽象工厂最终生产出来的对象)
  • 产品的实现类
  • 工厂的抽象类(用于定义按某种规则划分的工厂)
  • 工厂的实现类

举个例子:

 1abstract class MobilePhone {
 2    abstract call(): void;
 3}
 4
 5class iPhoneCls implements MobilePhone {
 6    call() {
 7        console.log("Apple!");
 8    }
 9}
10
11class LumiaCls implements MobilePhone {
12    call(): void {
13        console.log("Lumia!");
14    }
15}
16
17abstract class Laptop {
18    abstract reboot(): void
19}
20
21class MacBookCls implements Laptop {
22    reboot(): void {
23        console.log("MacBook Reboot");
24    }
25}
26
27class SurfaceCls implements Laptop {
28    reboot(): void {
29        console.log("Surface Reboot");
30    }
31}
32
33abstract class Electronic {
34    abstract createPhone(): MobilePhone;
35
36    abstract createLaptop(): Laptop;
37}
38
39class Apple implements Electronic {
40    createPhone(): MobilePhone {
41        return new iPhoneCls();
42    }
43
44    createLaptop(): Laptop {
45        return new MacBookCls();
46    }
47}
48
49class Microsoft implements Electronic {
50    createPhone(): MobilePhone {
51        return new LumiaCls();
52    }
53
54    createLaptop(): Laptop {
55        return new SurfaceCls();
56    }
57}
58
59interface Constructable<T> {
60    new(): T;
61}
62
63class Factory {
64    static createFactory<T>(cls: Constructable<T>): T {
65        return new cls();
66    }
67}
68
69// 消费场景
70
71Factory.createFactory(Apple).createPhone().call();
72Factory.createFactory(Apple).createLaptop().reboot();
73
74Factory.createFactory(Microsoft).createPhone().call();
75Factory.createFactory(Microsoft).createLaptop().reboot();

iPhoneClsLumiaClsMacBookClsSurfaceCls 分别实现了对应的产品抽象类。AppleMicrosoft 分别实现了对应的工厂抽象类。最后使用Factory的静态方法createFactory来创建对应工厂。这里的 interface Constructable 用于使参数cls可被new,否则ts会报错(当然直接new对应的工厂类也行,但最好避免,因为new也是一种dead code)。

整个消费场景中,没有一行代码与“实现类”有关,对于一个产品,我们只要知道其工厂类,就能直接创建对应产品。抽象工厂的优点显而易见,就是良好的封装性,关于任何实现类我都不需要关心,我只关心接口(interface 或 abstract class)。例如每次生产每次iPhone时都需要记一次log,这个操作可以放在工厂类中,高层模块完全无需参与。

实际开发中,只要是有相同规则的对象,都可以使用抽象工厂模式。例如开发一款跨平台软件时,针对不同平台就可以使用抽象工厂模式对平台相关API进行封装,这样高层模块完全无需知晓底层平台,一切API都靠工厂产出,对于不用平台则使用不同产品类去处理。