接着上一篇《JavaScript设计模式第2篇:工厂模式》,今天我们来看工厂模式的最后一种:抽象工厂。
有了前一节工厂方法模式的基础,抽象工厂其实很类似,只不过工厂方法针对的是一个产品等级结构,而抽象工厂针对的是多个产品等级结构。
我们先来解释 2 个概念:产品等级结构和产品族。
产品等级结构:
产品族:
抽象工厂模式,官方定义,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
抽象工厂模式包含如下 4 种角色:
概念还是太生涩了,下面看例子。
上一节我们的汽车厂商只能生产 Car,但是我们知道,很多汽车厂商也可以生产发动机 Engine,这就是两个产品等级结构,一个产品族。
我们先定义一个汽车厂商的抽象工厂 automakerFactory,提供 2 个抽象方法 createCar 和 createEngine:
class AutomakerFactory {
createCar () {
throw new Error('不能调用抽象方法,请自己实现');
}
createEngine () {
throw new Error('不能调用抽象方法,请自己实现');
}
}
复制代码
然后定义具体工厂实现抽象工厂:
class BenzFactory extends AutomakerFactory {
createCar () {
return new BenzCar();
}
createEngine () {
return new BenzEngine();
}
}
class AudiFactory extends AutomakerFactory {
createCar () {
return new AudiCar();
}
createEngine () {
return new AudiEngine();
}
}
复制代码
定义抽象产品类 Car 和 具体产品类 BenzCar、AudiCar:
class Car {
drive () {
throw new Error('不能调用抽象方法,请自己实现');
}
}
class BenzCar extends Car {
drive () {
console.log('Benz drive');
}
}
class AudiCar extends Car {
drive () {
console.log('Audi drive');
}
}
复制代码
定义抽象产品类 Engine 和 具体产品类 BenzEngine、AudiEngine:
class Engine {
start () {
throw new Error('不能调用抽象方法,请自己实现');
}
}
class BenzEngine extends Engine {
start () {
console.log('Benz engine start');
}
}
class AudiEngine extends Engine {
start () {
console.log('Audi engine start');
}
}
复制代码
如上,抽象工厂需要的 4 种角色就创建完成了,我们来看一下实例化过程:
let benz = new BenzFactory();
let benzCar = benz.createCar();
let benzEngine = benz.createEngine();
let audi = new AudiFactory();
let audiCar = audi.createCar();
let audiEngine = audi.createEngine();
benzCar.drive(); // Benz drive
benzEngine.start(); // Benz engine start
audiCar.drive(); // Audi drive
audiEngine.start(); // Audi engine start
还是通过 UML 类图来加深一下理解:
通过抽象工厂我们解决了什么问题呢?
假设现在需要增加另一个厂商 BMW,我们需要做些什么:
class BMWFactory extends AutomakerFactory {
createCar () {
return new BMWCar();
}
createEngine () {
return new BMWEngine();
}
}
复制代码
class BMWCar extends Car {
drive () {
console.log('BMW drive');
}
}
class BMWEngine extends Engine {
start () {
console.log('BMW engine start');
}
}
复制代码
let bmw = new BMWFactory();
let bmwCar = bmw.createCar();
let bmwEngine = bmw.createEngine();
bmwCar.drive(); // BMW drive
bmwEngine.start(); // BMW engine start
可以看到,和工厂方法一样,也不需要修改已有工厂类,只需要新增自己的具体工厂类和具体产品类就可以了,也符合“开闭原则”,只不过和工厂方法不同的是,抽象工厂针对的是一类产品族中的不同产品等级结构这种场景,而工厂方法只是针对于一种产品等级结构的场景。
当然它的缺点也和工厂方法一样,不断的添加新产品会导致类越来越多,增加了系统复杂度。