设计模式系列 --工厂模式

170 阅读4分钟

工厂模式主要有两个,工厂方法模式、抽象工厂,就先从简单工厂模式开始。

简单工厂模式

又称静态工厂方法。不属于 23 种规范里的,但可以当做跳板,渐进式学习。

类图

image.png

核心

要点:

  • 根据参数的不同返回不同的类的实例
  • 有一个专门负责创建的工厂类
  • 被创建的实例有共同的父类

优点

  • 根据给定的信息确定生成那个类。
  • 工厂只负责创建,实现责任的划分,单一职责。
  • 调用方不与具体的类相关联,只负责接口编程,符合迪米特法则。

缺点:

  • 一但要添加产品就得更改工厂的代码,不符合开闭原则。

代码示例

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()

image.png

工厂方法模式

又称为多态性工厂模式。可以看到,在简工厂模式中,过多的产品将会导致工厂使用到大量的 if else 语句,导致可扩展性不太友好。if else 在确定的情况下是可以使用的。

类图

工厂模式在简单方法模式的基础上,解决不符合开闭原则这一缺点。如何去解决,就是让核心的工厂不再去创建类,而是将类的实例化延迟到子类创建。

image.png 对比简单工厂,可以看到,多出来的工厂具体类和抽象类,具体工厂的职责更单一了(工厂的每个方法相当于要怎样创建具体的产品,抽象工厂方法概念),具体产品类实现产品接口,具体工厂类实现抽象工厂接口,这样调用方又能面相接口编程了。

核心

要点

  • 子类提供挂钩。基类确定方法,子类缺省的实现,转移实现过程到子类

优势

  • 符合开闭原则,实现可扩展功能。
  • 符合依赖倒置原则,面向接口编程,依赖于抽象不依赖于具体。
  • 符合里式替换原则,运行时是以具体类代替父类运行。
  • 多态性,与特定的应用无关。
  • 适合更复杂的层次结构

理解

  • 产品如何实现不关心,调用者只管调用,因为接口已经确定。
  • 解耦框架,
  • 多态性。

缺点

  • 抽象难度提升(兴一利必升一弊)

代码示例


// 假设 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()

image.png

可以看到,将核心的创建过程转移到了具体工程类,要是想改或者想添加类,就去加具体的产品类和工厂类,而不需要改原有的代码,也不需要根据传参来定义创建的类,达到符合开闭的目的。

抽象工厂模式

在工厂方法的基础上添加产品族概念,因为工厂方法模式只能创建单一产品。

类图

image.png 现在有华为小米两个工厂,在工厂方法模式一个工厂创建一种产品的基础上,将抽象工厂改造,实现创建多种产品,实现一个工厂可以创建一整套产品族的方式。

如果只有一个产品的时候,那就工厂方法模式了。

核心

要点:

  • 产品族概念:小米手机、小米电视机都属于小米工厂的,由同一工厂创建。
  • 产品等级结构,小米手机和华为手机就是同一等级,继承同一父类。

优势

  • 分离接口与实现,面向接口编程。

缺点

  • 难度更进一步提升,系统变得更复杂
  • 扩展困难。

使用场景

  • 需要一套替换的时候可以考虑到抽象工厂,不改变业务逻辑就能大换血。

代码


// 假设 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()

image.png