定义

工厂方法模式定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个, 工厂方法让类把实例化推迟到子类

设计原则

  • 依赖倒置原则: 要依赖抽象, 不要依赖具体类

依赖: 当你实例化一个对象时,就是在依赖他的具体类, 此案例中若是披萨店直接实例化披萨, 披萨店就依赖于披萨
高层组件与低层组件: 高层组件是由低层组件定义其行为的类, 比如此案例中披萨店属于高层组件, 因为它的行为是由披萨定义的
这个原则说明了: 不能让高层组件依赖于低层组件, 而且, 不管是高层还是低层, 两者都应该依赖于抽象

避免违背依赖倒置原则:

  • 变量不可以持有具体类的引用
  • 不要让类派生至具体类
  • 不要覆盖基类中已实现的方法

工厂方法前结构:
image.png
工厂方法后:
image.png

案例

背景: 披萨店有很多种的pizza, 需要根据顾客的类型去制作相应类型的披萨, 后续加工的步骤是一样的, 需要设计出种类可拓展, 可能增加或者减少的披萨类
image.png

原本的披萨类, 增加或减少披萨种类, orderPizza方法都需要改变, 违背了对修改关闭的原则

  1. public class PizzaStore {
  2. private Pizza pizza;
  3. public void orderPizza(String type) {
  4. if ("cheese".equals(type)) {
  5. pizza = new CheessePizza();
  6. } else if ("clam".equals(type)) {
  7. pizza = new ClamPizza();
  8. }
  9. pizza.prepare();
  10. pizza.bake();
  11. pizza.box();
  12. }
  13. }

简单工厂模式

改成简单工厂模式, 抽取出变化的部分(制造披萨实例, 且这部分抽取出来后可以被不止是披萨店用)
image.png
SimplePizzaFactory

  1. public class SimplePizzaFactory {
  2. public Pizza createPizza(String type) {
  3. if ("cheese".equals(type)) {
  4. return new CheessePizza();
  5. } else if ("clam".equals(type)) {
  6. return new ClamPizza();
  7. }
  8. return null;
  9. }
  10. }

PizzaStore, 还是通过持有factory实例, 实例可以通过构造方法传入

  1. public class PizzaStore {
  2. private SimplePizzaFactory factory;
  3. public PizzaStore(SimplePizzaFactory factory) {
  4. this.factory = factory;
  5. }
  6. public void orderPizza(String type) {
  7. Pizza pizza = factory.createPizza(type);
  8. pizza.prepare();
  9. pizza.bake();
  10. pizza.box();
  11. }
  12. }

工厂方法模式

背景2: 现在有纽约的和芝加哥的披萨店, 制作的是披萨因地域不同
将PizzaStore中的createPizza改为抽象方法, 由子类不同的披萨工厂去实现, orderPizza只需调用pizza即可, 无需知道具体的类型

  1. public abstract class PizzaStore {
  2. public void orderPizza(String type) {
  3. Pizza pizza = createPizza(type);
  4. pizza.prepare();
  5. pizza.bake();
  6. pizza.box();
  7. }
  8. protected abstract Pizza createPizza(String type);
  9. }

NYPizzaFactory继承PizzaStore, 重写createPizza方法, 制作出自己地域的pizza

  1. public class NYPizzaFactory extends PizzaStore {
  2. @Override
  3. protected Pizza createPizza(String type) {
  4. if ("cheese".equals(type)) {
  5. return new NYCheessePizza();
  6. } else if ("clam".equals(type)) {
  7. return new NYClamPizza();
  8. }
  9. return null;
  10. }
  11. }

工厂方法让子类决定要实例化的类是哪一个
image.png

抽象工厂模式

抽象工厂模式提供一个接口, 用于创建相关或依赖对象的家族, 而不需要明确指定具体类
背景3: 现在不同的工厂需要用不同的原料来制造披萨
image.png

PizzaIngredientFactory定义一个抽象工厂

  1. public interface PizzaIngredientFactory {
  2. Dough createDough();
  3. Sauce createSauce();
  4. }

NYIngredientFactory具体工厂来实现createDough(),createSauce()方法, 返回的Dough,Sauce都为抽象类型

  1. public class NYIngredientFactory implements PizzaIngredientFactory {
  2. @Override
  3. public Dough createDough() {
  4. return new NYDough();
  5. }
  6. @Override
  7. public Sauce createSauce() {
  8. return new NYSauce();
  9. }
  10. }

制作披萨则调用抽象工厂的创造原料的方法

  1. public class NYClamPizza extends Pizza {
  2. private PizzaIngredientFactory pizzaIngredientFactory;
  3. public NYClamPizza(PizzaIngredientFactory pizzaIngredientFactory) {
  4. this.pizzaIngredientFactory = pizzaIngredientFactory;
  5. }
  6. @Override
  7. public void prepare() {
  8. // 工厂是抽象的, 可以根据传入工厂的不同, 使用不同工厂的制造方法
  9. Dough dough = pizzaIngredientFactory.createDough();
  10. Sauce sauce = pizzaIngredientFactory.createSauce();
  11. }
  12. }

工厂方法和抽象工厂的对比

  1. 本质对比
  • 整个工厂方法模式, 只不过是通过子类来创建对象; 用这种做法, 客户只需要知道他们所使用的抽象类型就可以了, 而由子类来负责和决定具体类型, 即将客户从具体类型中解耦;
  • 抽象工厂模式用来创建一个产品家族的抽象类型, 这个类型的子类定义了产品被产生的方法, 要使用这个工厂必须先实例化它, 同工厂方法一样, 也是将客户从使用的实际具体产品中解耦

2.实现方式

  • 工厂方法使用继承, 把对象创建委托给子类, 子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合, 对象的创建被实现在工厂接口所暴露出来的方法中

3.使用场景

  • 将客户代码从需要实例化的具体类中解耦, 或者目前还不知道将来要实例化哪些具体类时, 可以使用工厂方法模式
  • 当需要创建产品家族和想让制造相关产品集合起来时, 可以使用抽象工厂模式