创建型模式：工厂模式（包含抽象工厂）
简介工厂模式是讲需要实例化的代码提取出来，放到一个专门的类中去做管理和维护，达到和主体代码的解耦，从而提高项目的扩展性和维护性。将创建实例与调用实例进行解耦！简单来说就是把new操作（创建实例）放到一个专门的地方统一管理，而不是在代码中耦合使用，方便管理和扩展。工厂模式有三种用法：简单工厂模式、工厂方法模式、抽象工厂模式简单工厂模式简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例。比如我们需要建一个饭店，这个饭店需要招三名厨师，他们要有各自的拿手菜：水煮牛肉、青椒炒蛋、炒米粉。为了能方便的招聘厨师，我定了一个要求，厨师必须会：开火、颠勺、关火、出菜于是我写了一个接口来要求这些厨师。abstract class Cooks { abstract openFire(): void; //开火 abstract dianshao(): void; //颠勺 abstract shutFire(): void; //关火 abstract accomplish(): string; //出菜 }通过这个接口我找到三名厨师：小王、小明、小红class xw extends Cooks { openFire() { console.log("开火，开始准备水煮牛肉"); } dianshao() { console.log("颠勺"); } shutFire() { console.log("关火，水煮牛肉快好了"); } accomplish() { return "水煮牛肉"; } } class xm extends Cooks { openFire() { console.log("开火，开始准备青椒炒蛋"); } dianshao() { console.log("颠勺"); } shutFire() { console.log("关火，青椒炒蛋快好了"); } accomplish() { return "青椒炒蛋"; } } class xh extends Cooks { openFire() { console.log("开火，开始准备炒米粉"); } dianshao() { console.log("颠勺"); } shutFire() { console.log("关火，炒米粉快好了"); } accomplish() { return "炒米粉"; } }现在我厨师有了，就需要考虑，怎么让厨师去做菜，饭店都是有人专门管理这个事情的，它负责跟厨师和外部沟通，我们称之为厨房管理员这个管理员就可以写成工厂模式，它接受一个菜名，便叫厨师做菜，厨师做完了，就可以出菜了。class KitchenManager { private cook: Cooks | undefined; cookFood(foodName: string) { if (foodName === "水煮牛肉") { this.cook = new xw(); } else if (foodName === "青椒炒蛋") { this.cook = new xm(); } else if (foodName === "炒米饭") { this.cook = new xh(); } else { throw new Error("这个菜做不了"); } //具体做菜逻辑 this.cook.openFire(); this.cook.dianshao(); this.cook.shutFire(); return this.cook.accomplish(); } }此时一个简单工厂模式就搭建完成，我们甚至可以把具体的做菜逻辑封装到每个厨师的类上面，我们需要负责调用accomplish拿到做好的菜品即可。当我们需要增加新的菜品时，外部的使用代码并不用改动逻辑，只是传入的参数不同而已，而我们只需要改动厨房管理员这部分代码，比如增加更多的else if判断，或者招聘更多的厨师，以满足客户需求。简单工厂也称之为静态工厂，因为上述的管理员代码，完全可以转成静态方法使用，功能不受影响。但是简单工厂也是有缺点的，虽然整体代码满足开闭原则，但是工厂本身是违背开闭原则的，因为每增加一个新厨师，或者新菜品，工厂一定会增加一个判断，比如else if。工厂方法模式工厂方法模式指定义一个创建对象的接口，但是让实现这个接口的类来决定实例化哪个类，工厂方法让类的实例化推迟到子类中进行。它用于解决简单工厂中特别多的if判断的问题，当我们的店铺越来越大的时候，菜品和做菜师傅也越来越多，那么KitchenManager承担了特别多的逻辑，成了一个全能工厂，这显然不太好维护。我们将每个菜品分类为不同的菜系，通过不同菜系工厂去调用对应的厨师，再产出实际的食物。然后管理员只提供不同菜系的接口，由顾客去选择。interface Factory { create(name: string): void; }接口抽象定义好后，我们的工厂就要对其进行自己的具体实现，由于懒得写厨师类了，厨师的代码就不要当真，因为如果要按照真实逻辑，一个厨师可能会多个菜品，他们自己会有对菜品判断是否可以制作的方法，这些都是细节，知道逻辑就行了。//川系菜品 class CFactory implements Factory { private cook: Cooks | undefined; create(name: string) { if (name === "水煮牛肉") { this.cook = new xw(); } else if (name === "水煮鱼") { this.cook = new xw(); } else { throw new Error("这个菜做不了"); } //具体做菜逻辑 this.cook.openFire(); this.cook.dianshao(); this.cook.shutFire(); return this.cook.accomplish(); } } //湘系菜品 class XFactory implements Factory { private cook: Cooks | undefined; create(name: string) { if (name === "青椒炒蛋") { this.cook = new xm(); } else if (name === "霸王别姬") { this.cook = new xm(); } else { throw new Error("这个菜做不了"); } //具体做菜逻辑 this.cook.openFire(); this.cook.dianshao(); this.cook.shutFire(); return this.cook.accomplish(); } } // 粤系菜品 class YFactory implements Factory { private cook: Cooks | undefined; create(name: string) { if (name === "炒米粉") { this.cook = new xh(); } else if (name === "炒河粉") { this.cook = new xh(); } else { throw new Error("这个菜做不了"); } //具体做菜逻辑 this.cook.openFire(); this.cook.dianshao(); this.cook.shutFire(); return this.cook.accomplish(); } }我实现了三个菜系的工厂，他们都会根据Factory抽象实现自己的具体实现。工厂创建好后，怎么去调用工厂由顾客去决定，而管理员提供的是不同工厂的调用接口。class KitchenManager { //湘菜 public xc(foodName: string) { return new XFactory().create(foodName); } //川菜 public cc(foodName: string) { return new CFactory().create(foodName); } //粤菜 public yc(foodName: string) { return new YFactory().create(foodName); } }这样就写省去了if-else的代码，虽然实现了开闭原则，但是会产生大量的工厂。再举个例子：比如食堂打饭，学校一开始只有一个窗口，这个窗口里有：水煮牛肉，青椒炒蛋，炒米粉；学生去打饭的时候只能排队从这一个窗口里购买食物，这个窗口里面的人忙的不可开交。一会拿炒鸡蛋，一会拿炒米粉等等...(简单工厂)于是为了减少里面打菜人的负担，学校又新增了两个窗口，现在是三个窗口，每个窗口对应一个菜，学生自己选择自己要吃什么，在哪个窗口排队，从而减少了窗口内人的负担。（工厂方法）抽象工厂模式抽象工厂模式是指提供一个创建一系列相关或相互依赖对象的接口，无需指定他们具体的类。这种模式有点类似于多个供应商负责提供一系列类型的产品。比如都是青椒炒蛋，有的厨师用辣的青椒做，有的厨师用不辣的青椒做。他们都属于青椒炒蛋，但是是不同的口味，这个口味可以看成是产品等级。A厨师可以做：青椒炒蛋、霸王别姬，但是是辣的；B厨师也可以做：青椒炒蛋、霸王别姬，但是是不辣的；他们都是同一菜系：湘菜；但是有不同的口味。顾客来吃饭时，根据自己的口味调用不同的工厂实现，比如能吃辣的和不能吃辣的，都可以点青椒炒蛋。只要说明能不能吃辣，管理员就能调用工厂提供的方法实现。这时我们可以发现，工厂方法面对的是多个产品同一个产品等级（产品结构）；而抽象工厂是面对多个产品多个产品等级。多个产品等级，那么表示这个产品会被多个实例去实现，为了规范，就需要为这个产品定义一个接口，工厂依赖这个接口，实例也依赖于这个接口。首先定义了两个菜品的接口：//青椒炒蛋接口 interface QJCD { name: string; getName(): string; } //霸王别姬的接口 interface BWBJ { name: string; getName(): string; }再对菜品进行具体的实现：//辣的青椒炒蛋 class LQJCD implements QJCD { name = "辣的青椒炒蛋"; getName() { return this.name; } } //不辣的青椒炒蛋 class BLQJCD implements QJCD { name = "不辣的青椒炒蛋"; getName() { return this.name; } } //辣的霸王别姬 class LBWBJ implements BWBJ { name = "辣的霸王别姬"; getName() { return this.name; } } //不辣的霸王别姬 class BLBWBJ implements BWBJ { name = "不辣的霸王别姬"; getName() { return this.name; } } 不同厨师使用不同的菜品实现，为了规范，先定义一个规范：两个厨师都需要对这两个菜品进行实现，所以先定义一个接口，而具体创建实例的方法是一样的，所以可以create直接继承。//工厂抽象 abstract class Factory { create(name: string) { if (name === "青椒炒蛋") { return this.createQJCD(); } else if (name === "霸王别姬") { return this.createBWBJ(); } else { throw new Error("这个菜做不了"); } } abstract createQJCD(): QJCD; abstract createBWBJ(): BWBJ; }两个厨师：//A厨师 class ACookFactory extends Factory { createQJCD() { return new LQJCD(); } createBWBJ() { return new LBWBJ(); } } //b厨师 class BCookFactory extends Factory { createQJCD() { return new BLQJCD(); } createBWBJ() { return new BLBWBJ(); } }现在具体的厨师也有了，我们需要一个类来统一管理这个厨师，比如有的人要不辣的，我就抛出不辣的厨师。//湘菜工厂的工厂 class XCFactoryFactory { static getFactory(L: boolean): Factory { if (L) { return new ACookFactory(); } else { return new BCookFactory(); } } }最终通过管理调用：//管理员 class KitchenManager { //湘菜 public xc(foodName: string, L: boolean) { const factory: Factory = XCFactoryFactory.getFactory(L); const food = factory.create(foodName); return food.getName(); } }抽象工厂可以简单高效的创建多个产品与多个产品等级结构的问题，或者拿官话来讲：简单高效的实现了多个产品族与产品等级结构创建问题。但是我们再细品代码：当我们新增一个品类的时候，除了：青椒炒蛋，霸王别姬；我们还需要新增一个冬笋炒肉，此时你得在工厂抽象中声明抽象，声明之后子类实现上也得写一份具体实现，也就是说A、B两个厨师都得实现这个方法。增加了系统理解难度。使用抽象工厂模式一般要满足以下条件。系统中有多个产品族，每个具体工厂创建同一族但属于不同等级结构的产品。系统一次只可能消费其中某一族产品，即同族的产品一起使用。由于抽象工厂比较难理解，但是它又是面试官喜欢问的一个问题，当他问你工厂模式的时候，大概率问的就是抽象工厂模式，等有机会我在弄几个实战例子加深理解。