Skip to content

工厂方法模式和抽象工厂模式

定义

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

设计原则

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

依赖: 当你实例化一个对象时,就是在依赖他的具体类, 此案例中若是披萨店直接实例化披萨, 披萨店就依赖于披萨

**高层组件与低层组件: **高层组件是由低层组件定义其行为的类, 比如此案例中披萨店属于高层组件, 因为它的行为是由披萨定义的

这个原则说明了: 不能让高层组件依赖于低层组件, 而且, 不管是高层还是低层, 两者都应该依赖于抽象

避免违背依赖倒置原则:

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

工厂方法前结构:

1744858637385

工厂方法后:

1744858644332

案例

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

1744858652684

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

Java
public class PizzaStore {

  private Pizza pizza;

  public void orderPizza(String type) {
    if ("cheese".equals(type)) {
      pizza = new CheessePizza();
    } else if ("clam".equals(type)) {
      pizza = new ClamPizza();
    }

    pizza.prepare();
    pizza.bake();
    pizza.box();
  }
}

简单工厂模式

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

1744858659578

SimplePizzaFactory

Java
public class SimplePizzaFactory {

  public Pizza createPizza(String type) {
    if ("cheese".equals(type)) {
      return new CheessePizza();
    } else if ("clam".equals(type)) {
      return new ClamPizza();
    }
    return null;
  }
}

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

Java
public class PizzaStore {

  private SimplePizzaFactory factory;

  public PizzaStore(SimplePizzaFactory factory) {
    this.factory = factory;
  }

  public void orderPizza(String type) {
    Pizza pizza = factory.createPizza(type);

    pizza.prepare();
    pizza.bake();
    pizza.box();
  }
}

工厂方法模式

背景2: 现在有纽约的和芝加哥的披萨店, 制作的是披萨因地域不同

将PizzaStore中的createPizza改为抽象方法, 由子类不同的披萨工厂去实现, orderPizza只需调用pizza即可, 无需知道具体的类型

Java
public abstract class PizzaStore {

  public void orderPizza(String type) {
    Pizza pizza = createPizza(type);

    pizza.prepare();
    pizza.bake();
    pizza.box();
  }

  protected abstract Pizza createPizza(String type);
}

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

Java
public class NYPizzaFactory extends PizzaStore {

  @Override
  protected Pizza createPizza(String type) {
    if ("cheese".equals(type)) {
      return new NYCheessePizza();
    } else if ("clam".equals(type)) {
      return new NYClamPizza();
    }
    return null;
  }
}

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

1744858669085

抽象工厂模式

抽象工厂模式提供一个接口, 用于创建相关或依赖对象的家族, 而不需要明确指定具体类

背景3: 现在不同的工厂需要用不同的原料来制造披萨

1744858676253

PizzaIngredientFactory定义一个抽象工厂

Java
public interface PizzaIngredientFactory {

  Dough createDough();

  Sauce createSauce();
}

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

Java
public class NYIngredientFactory implements PizzaIngredientFactory {

  @Override
  public Dough createDough() {
    return new NYDough();
  }

  @Override
  public Sauce createSauce() {
    return new NYSauce();
  }
}

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

Java
public class NYClamPizza extends Pizza {

  private PizzaIngredientFactory pizzaIngredientFactory;

  public NYClamPizza(PizzaIngredientFactory pizzaIngredientFactory) {
    this.pizzaIngredientFactory = pizzaIngredientFactory;
  }

  @Override
  public void prepare() {
    // 工厂是抽象的, 可以根据传入工厂的不同, 使用不同工厂的制造方法
    Dough dough = pizzaIngredientFactory.createDough();
    Sauce sauce = pizzaIngredientFactory.createSauce();
  }
}

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

  1. 本质对比
  • 整个工厂方法模式, 只不过是通过子类来创建对象; 用这种做法, 客户只需要知道他们所使用的抽象类型就可以了, 而由子类来负责和决定具体类型, 即将客户从具体类型中解耦;
  • 抽象工厂模式用来创建一个产品家族的抽象类型, 这个类型的子类定义了产品被产生的方法, 要使用这个工厂必须先实例化它, 同工厂方法一样, 也是将客户从使用的实际具体产品中解耦
  1. 实现方式
  • 工厂方法使用继承, 把对象创建委托给子类, 子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合, 对象的创建被实现在工厂接口所暴露出来的方法中
  1. 使用场景
  • 将客户代码从需要实例化的具体类中解耦, 或者目前还不知道将来要实例化哪些具体类时, 可以使用工厂方法模式
  • 当需要创建产品家族和想让制造相关产品集合起来时, 可以使用抽象工厂模式