【设计模式】工厂模式

2022/03/31 设计模式 共 2722 字,约 8 分钟

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。

其中简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。

除此之外,我们讲解的重点也不是原理和实现,因为这些都很简单,重点还是带你搞清楚应用场景:什么时候该用工厂模式?相对于直接 new 来创建对象,用工厂模式来创建究竟有什么好处呢?

简单工厂

我们举个例子,在微信支付中,支付方式有多种,包括H5、APP、Native、JSAPI等。

在创建订单的时候,我们需要根据支付方式去实现不同的创建订单的逻辑,这里我们就可以使用简单工厂模式。

代码如下:

@Data
@ToString
public class Order {
    private Long orderId;
    private String orderName;
}

@Data
@ToString
public class H5Order extends Order {
}

@Data
@ToString
public class AppOrder extends Order {
}

简单工厂实现

public class OrderSimpleFactory {

  // 根据类型创建订单
  public static Order createOrder(String orderType) {
    Order order = null;
    if ("h5".equals(orderType)) {
      order = new H5Order();
    } else if ("app".equals(orderType)) {
      order = new AppOrder();
    }
    return order;
  }
}

在上面的代码实现中,我们每次调用createOrder(),都会创建一个新的Order,为了节省内存和对象创建的时间,我们可以将 Order事先创建好缓存起来。当调用 createOrder(),可以直接从缓存中获取使用。

private static final Map<String, Order> orderMap = new HashMap<>();
    static {
        orderMap.put("h5", new H5Order());
        orderMap.put("app", new AppOrder());
    }

测试类

// 简单工厂
@Test
public void test() {
  Order order = OrderSimpleFactory.createOrder("h5");
  // 结果为 H5Order
  log.info(order.toString());
}

工厂方法

如果我们非得要将 if 分支逻辑去掉,那该怎么办呢?比较经典处理方法就是利用多态。按照多态的实现思路,对上面的代码进行重构。重构之后的代码如下所示:

方法工厂实现

public interface OrderMethodFactory {

    Order createOrder();
}

public class AppOrderFactory implements OrderMethodFactory {

    @Override
    public Order createOrder() {
        Order order = new AppOrder();
        // 自定义逻辑......
        return order;
    }
}

public class H5OrderFactory implements OrderMethodFactory {

    @Override
    public Order createOrder() {
        Order order = new H5Order();
        // 自定义逻辑......
        return order;
    }
}

实际上,这就是工厂方法模式的典型代码实现,工厂方法模式比起简单工厂模式更加符合开闭原则。

从上面的工厂方法的实现来看,一切都很完美,但是实际上存在挺大的问题。问题存在于这些工厂类的使用上。我们还是需要判断orderType去选择相应的OrderFactory,也就是if else逻辑还存在。

我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。

工厂的工厂

public class OrderMethodFactoryMap {

    // 不需要每次都创建新的工厂类对象
    private static final Map<String, OrderMethodFactory> factoryMap = new HashMap<>();

    static {
        factoryMap.put("h5", new H5OrderFactory());
        factoryMap.put("app", new AppOrderFactory());
    }

    public static OrderMethodFactory getOrderFactory(String orderType) {
        return factoryMap.get(orderType);
    }
}

那什么时候该用工厂方法模式,而非简单工厂模式呢?

当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。

除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同的对象。如果我们使用简单工厂模式来实现,就只能选择第一种包含 if 分支逻辑的实现方式。如果我们还想避免烦人的 if-else 分支逻辑,这个时候,我们就推荐使用工厂方法模式。

抽象工厂

在工厂方法中,如果我们还需要创建其他类型的订单,比如支付宝订单,那么我们需要再定义一个工厂,然后再增加4个工厂实现类。而我们知道,过多的类也会让系统难维护。这个问题该怎么解决呢?

抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象,这样就可以有效地减少工厂类的个数。

代码如下:

public interface OrderAbstractFactory {

    // 创建微信订单
    Order createWxOrder();

    // 创建支付宝订单
    AliOrder createAliOrder();
}

//省略实现类,同工厂方法

总结

使用场景

  • 代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象
  • 尽管我们不需要根据不同的类型创建不同的对象,但是,单个对象本身的创建过程比较复杂

参考标准

以下是判断要不要使用工厂模式的最本质的参考标准:

  • 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
  • 代码复用:创建代码抽离到独立的工厂类之后可以复用。
  • 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
  • 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁

文档信息

搜索

    Table of Contents