设计模式-创建型-工厂模式

235 阅读7分钟

工厂模式

在现实生活中,工厂是负责生产产品的,比如早上吃的面包, 但是如果我们从和面开始制作一个面包时间成本就会很高。 所以我们更倾向于直接去买一个,而为什么工厂就能生产效率这么高呢? 很显然工厂,有专门生产面包的机器,我们只需要按照我们的要求设定就能得到我们想要的面包。 而在程序中,工厂模式就是生产我们需要的对象,直接提供给你面包,而不是需要你自己制作。

分类

工厂模式分为两种

  1. 工厂方法模式
  2. 抽象工厂模式

工厂方法模式

定义

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

工厂方法是一种创建型设计模式, 解决了在不指定具体类的情况下创建产品对象的问题。

适合场景

  1. 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。 工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。 例如,你的面包需要满足不同口味的客户。而你需要制作的面包是未知的。 当客户需要新的手撕面包时候,你就可以创建一个手新的手撕面包子类,并重写工厂方法即可。

  2. 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。 例如:用户说你的面包口味太淡了,而且吃不饱,需要加一根烤肠。 你直接重写面包制作方法,加上一根烤肠即可。

工厂方法模式优缺点

优点

  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。

缺点

  • 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。

与简单工厂模式的区别

简单工厂模式 描述了一个类, 它拥有一个包含大量条件语句的构建方法, 可根据方法的参数来选择对何种产品进行初始化并将其返回。

人们通常会将简单工厂与普通的工厂或其它创建型设计模式混淆。 在绝大多数情况下, 简单工厂是引入工厂方法或抽象工厂模式时的一个中间步骤

简单工厂通常没有子类。 但当从一个简单工厂中抽取出子类后, 它看上去就会更像经典的工厂方法模式了。 顺便提一句, 如果你将一个简单工厂声明为 abstract 类型, 它并不会神奇地变成抽象工厂模式。

代码

问题:假设你正在开发一款物流管理应用。 最初版本只能处理火车运输, 因此大部分代码都在位于名为 火车 的类中。一段时间后,广受好评,开拓了海外市场,开始使用轮船运输


/**
 * 运输工具
 */
interface Transport {
  // 必须有 work 方法,让资本家剥削
  work(): void;
}

abstract class Creator {
  public abstract createMethod(): Transport;
}

// 造船工厂
class ShipFactory implements Creator {
  createMethod() {
    return new Ship()
  }
}

// 造火车工厂
class TrainFactory implements Creator {
  createMethod() {
    return new Train()
  }
}

const trainFactory = new TrainFactory()
const shipFactory = new ShipFactory()
// '造个火车来拉货'
const train = trainFactory.createMethod()
train.work()
// 需要运到国外了
// 造个轮船来拉货
const ship = shipFactory.createMethod()
ship.work()

// 运输工具之 火车
class Train implements Transport {
  work() {
    // 如果需要在 火车 工作前假如一些其它工作,即可在此修改而不用修改 创建者 或者 具体产品
    // 例如工作前先检查 火车 是否能够正常工作
    // console.log('一切正常!我要工作了~')
    console.log('咣哧 咣哧 咣哧 咣哧! 开始工作了')
  }
}

// 运输工具之 轮船
class Ship implements Transport {
  work() {
    console.log('呜~~ 呜~~ 呜~~! 开始工作了')
  }
}

抽象工厂模式

定义

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

例如:当你需要为你的新家购置家具时,通常桌椅是配套售卖的,这样能确保风格和大小匹配。 但是工厂在生产桌椅时却是分开生产的,这样能提高生产效率。 而为了确保最后得到的桌椅大小风格都是配套的,我们需要抽象一个能生产桌椅的类。

抽象工厂模式适合应用场景

  1. 如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。

  2. 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。

抽象工厂模式优缺点

优点

  • 你可以确保同一工厂生成的产品相互匹配。
  • 你可以避免客户端和具体产品代码的耦合。
  • 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
  • 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。

缺点

  • 由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式 (更灵活但更加复杂)。
  • 生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 生成器则允许你在获取产品前执行一些额外构造步骤。
  • 抽象工厂模式通常基于一组工厂方法, 但你也可以使用原型模式来生成这些类的方法。
  • 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂来代替外观模式。
  • 你可以将抽象工厂和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
  • 抽象工厂、 生成器和原型都可以用单例模式来实现。

代码

问题:假设你正在开发一款家具商店模拟器:需要一系列相关产品, 例如桌子、椅子;需要不同风格如大气的中式、精美的欧式。 解决:首先, 抽象工厂模式建议为系列中的每件产品明确声明接口 (例如桌子、椅子)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的桌子都实现 桌子接口; 所有风格的椅子都实现 椅子 接口, 以此类推。

interface AbstractFurniture {
  createDesk(): Desk;
  createChair(): Chair;
}

class ChineseFurniture {
  createDesk() {
    return new ChineseDesk()
  }
  createChair() {
    return new ChineseChair()
  }
}

class EuropeFurniture {
  createDesk() {
    return new EuropeDesk()
  }
  createChair() {
    return new EuropeChair()
  }
}

const chineseFurniture = new ChineseFurniture()
const europeFurniture = new EuropeFurniture()
// 来一套中式家具
chineseFurniture.createDesk()
chineseFurniture.createChair()
// 来一套欧式家具
europeFurniture.createDesk()
europeFurniture.createChair()


interface Desk {
  // 桌子能放东西
  putOn(): void;
}

interface Chair {
  // 椅子能坐人
  sit(): void;
}

class ChineseDesk implements Desk {
  putOn() {
    console.log('中国传统桌子很庄重大气')
  }
}

class ChineseChair implements Chair {
  sit() {
    console.log('中国传统椅子很简约实用')
  }
}

class EuropeDesk implements Desk {
  putOn() {
    console.log('欧洲传统桌子很时尚奢侈')
  }
}

class EuropeChair implements Chair {
  sit() {
    console.log('欧洲传统椅子很精致华丽')
  }
}