工厂模式主要有两个,工厂方法模式、抽象工厂,就先从简单工厂模式开始。
简单工厂模式
又称静态工厂方法。不属于 23 种规范里的,但可以当做跳板,渐进式学习。
类图
核心
要点:
- 根据参数的不同返回不同的类的实例
- 有一个专门负责创建的工厂类
- 被创建的实例有共同的父类
优点
- 根据给定的信息确定生成那个类。
- 工厂只负责创建,实现责任的划分,单一职责。
- 调用方不与具体的类相关联,只负责接口编程,符合迪米特法则。
缺点:
- 一但要添加产品就得更改工厂的代码,不符合开闭原则。
代码示例
class PeopleFactory {
constructor() {
console.log('工厂建造完毕')
}
createPerson(type) {
if (type === 'A') {
return new PersonA()
} else if (type === 'B') {
return new PersonB
}
}
}
// 假定这两类人都要是实现接口,规定有 work sleep 方法
class PersonA {
work() {
console.log('最爱996')
}
sleep() {
console.log('只睡1小时')
}
}
class PersonB {
work() {
console.log('最爱955')
}
sleep() {
console.log('只睡12小时')
}
}
const peopleFactory = new PeopleFactory();
let personA = peopleFactory.createPerson('A')
personA.work()
personA.sleep()
工厂方法模式
又称为多态性工厂模式。可以看到,在简工厂模式中,过多的产品将会导致工厂使用到大量的 if else 语句,导致可扩展性不太友好。if else 在确定的情况下是可以使用的。
类图
工厂模式在简单方法模式的基础上,解决不符合开闭原则这一缺点。如何去解决,就是让核心的工厂不再去创建类,而是将类的实例化延迟到子类创建。
对比简单工厂,可以看到,多出来的工厂具体类和抽象类,具体工厂的职责更单一了(工厂的每个方法相当于要怎样创建具体的产品,抽象工厂方法概念),具体产品类实现产品接口,具体工厂类实现抽象工厂接口,这样调用方又能面相接口编程了。
核心
要点
- 子类提供挂钩。基类确定方法,子类缺省的实现,转移实现过程到子类
优势
- 符合开闭原则,实现可扩展功能。
- 符合依赖倒置原则,面向接口编程,依赖于抽象不依赖于具体。
- 符合里式替换原则,运行时是以具体类代替父类运行。
- 多态性,与特定的应用无关。
- 适合更复杂的层次结构
理解
- 产品如何实现不关心,调用者只管调用,因为接口已经确定。
- 解耦框架,
- 多态性。
缺点
- 抽象难度提升(兴一利必升一弊)
代码示例
// 假设 PeopleFactoryA,PeopleFactoryB都继承了工厂的抽象类,实现了 createFactory 方法。
class PeopleFactoryA {
constructor() {
console.log('工厂建造完毕')
}
createPeople() {
return new PersonA()
}
}
class PeopleFactoryB {
constructor() {
console.log('工厂建造完毕')
}
createPeople() { // 每个方法可以成为一个工厂个
return new PersonB()
}
}
// 假定这两类人都要是实现接口,规定有 work sleep 方法
class PersonA {
work() {
console.log('最爱996')
}
sleep() {
console.log('只睡1小时')
}
}
class PersonB {
work() {
console.log('最爱955')
}
sleep() {
console.log('只睡12小时')
}
}
const peopleFactoryA = new PeopleFactoryA();
let personA = peopleFactoryA.createPeople()
personA.work()
personA.sleep()
console.log('--------')
const peopleFactoryB = new PeopleFactoryB();
let personB = peopleFactoryB.createPeople()
personB.work()
personB.sleep()
可以看到,将核心的创建过程转移到了具体工程类,要是想改或者想添加类,就去加具体的产品类和工厂类,而不需要改原有的代码,也不需要根据传参来定义创建的类,达到符合开闭的目的。
抽象工厂模式
在工厂方法的基础上添加产品族概念,因为工厂方法模式只能创建单一产品。
类图
现在有华为小米两个工厂,在工厂方法模式一个工厂创建一种产品的基础上,将抽象工厂改造,实现创建多种产品,实现一个工厂可以创建一整套产品族的方式。
如果只有一个产品的时候,那就工厂方法模式了。
核心
要点:
- 产品族概念:小米手机、小米电视机都属于小米工厂的,由同一工厂创建。
- 产品等级结构,小米手机和华为手机就是同一等级,继承同一父类。
优势
- 分离接口与实现,面向接口编程。
缺点
- 难度更进一步提升,系统变得更复杂
- 扩展困难。
使用场景
- 需要一套替换的时候可以考虑到抽象工厂,不改变业务逻辑就能大换血。
代码
// 假设 PeopleFactoryA,PeopleFactoryB都继承了工厂的抽象类,实现了 createFactory 方法。
class XIAOMI {
constructor() {
console.log('XIAOMI工厂建造完毕')
}
createTv() {
return new XIAOMITV()
}
createPhone() {
return new XIAOMIPhone()
}
}
class HUAWEI {
constructor() {
console.log('HUAWEI工厂建造完毕')
}
createTv() {
return new HUAWEITV()
}
createPhone() {
return new HUAWEIPhone()
}
}
// 假定规定了手机类接口 play
class HUAWEIPhone {
play() {
console.log('玩华为手机')
}
}
class XIAOMIPhone {
play() {
console.log('玩小米手机')
}
}
// 假定规定了电视类接口 play open
class HUAWEITV {
close() {
console.log('打开我的华为电视')
}
open() {
console.log('关闭我的华为电视')
}
}
class XIAOMITV {
close() {
console.log('打开我的小米电视')
}
open() {
console.log('关闭我的小米电视')
}
}
const huaweiFactory = new HUAWEI();
let huaweiPhone = huaweiFactory.createPhone()
huaweiPhone.play()
let huaweiTv = huaweiFactory.createTv()
huaweiTv.open()
huaweiTv.close()
console.log('--------')
const xiaomiFactory = new XIAOMI();
let xiaomiPhone = xiaomiFactory.createPhone()
xiaomiPhone.play()
let xiaomiTv = xiaomiFactory.createTv()
xiaomiTv.open()
xiaomiTv.close()