一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。
其中简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。
除此之外,我们讲解的重点也不是原理和实现,因为这些都很简单,重点还是带你搞清楚应用场景:什么时候该用工厂模式?相对于直接 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 分支判断,动态地根据不同的类型创建不同的对象
- 尽管我们不需要根据不同的类型创建不同的对象,但是,单个对象本身的创建过程比较复杂
参考标准
以下是判断要不要使用工厂模式的最本质的参考标准:
- 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
- 代码复用:创建代码抽离到独立的工厂类之后可以复用。
- 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
- 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁
文档信息
- 本文作者:yindongxu
- 本文链接:https://iceblow.github.io/2022/03/31/%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)