1. 初衷
写这篇文章的初衷,介绍一下工厂模式,以及具体使用实例。
2. 简单介绍
首先明确一点就是工厂是用来干啥的,工厂是用来创建对象的。
工厂模式的作用
- 封装对象的创建过程: 工厂模式将对象的创建过程封装在工厂类中,使客户端代码不需要了解具体的构建过程,只需向工厂请求对象即可。
- 提高代码的可维护性: 工厂模式可以集中管理对象的创建,如果需要修改对象的构建方式,只需修改工厂类而不影响客户端代码。
- 提高代码的扩展性: 工厂模式允许添加新的产品类型而无需修改现有客户端代码。这样可以方便地扩展应用程序。
- 降低耦合度: 使用工厂模式可以降低客户端代码与具体产品类之间的耦合度,因为客户端只与工厂接口交互,不需要直接与产品类交互。
- 提高代码的重用性: 工厂模式可以在不同的地方使用相同的工厂来创建对象,提高了代码的重用性。
3. 简单工厂模式
3.1 请求快递实例,无设计模式情况
公共快递下单接口
java
public interface IExpressDelivery {
/**
* 快递下单
*/
OrderResponse createOrder(OrderRequest orderRequest);
}
京东快递下单
java
public class JDExpressDelivery implements IExpressDelivery{
@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
// 省略请求京东的下单api过程
OrderResponse orderResponse = new OrderResponse();
orderResponse.setBillNo("京东快递单号");
return orderResponse;
}
}
顺丰快递下单
java
public class SFExpressDelivery implements IExpressDelivery {
@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
// 省略请求顺丰的下单api过程
OrderResponse orderResponse = new OrderResponse();
orderResponse.setBillNo("顺丰快递单号");
return orderResponse;
}
}
圆通快递下单
java
public class YTExpressDelivery implements IExpressDelivery {
@Override
public OrderResponse createOrder(OrderRequest orderRequest) {
// 省略请求圆通的下单api过程
OrderResponse orderResponse = new OrderResponse();
orderResponse.setBillNo("圆通快递单号");
return orderResponse;
}
}
客户端调用
java
public class Test {
public static void main(String[] args) {
String type = "SF";
OrderResponse orderResponse = createOrderByType(type);
System.out.println("快递单号为"+orderResponse.getBillNo());
}
public static OrderResponse createOrderByType(String type) {
IExpressDelivery expressDelivery = null;
if (type.equals("JD")) {
expressDelivery = new JDExpressDelivery();
} else if (type.equals("SF")) {
expressDelivery = new SFExpressDelivery();
} else {
expressDelivery = new YTExpressDelivery();
}
OrderRequest orderRequest = new OrderRequest();
return expressDelivery.createOrder(orderRequest);
}
}
从客户端调用就可以看出客户端是需要依赖实现类的,当前举的例子实例化还是很简单的,如果实例化需要更复杂的代码客户端的代码也会看起来很臃肿。
3.2 请求快递实例,使用简单工厂模式
工厂
java
public class ExpressDeliveryFactory {
public static IExpressDelivery create(String deliveryType) {
IExpressDelivery expressDelivery = null;
if (deliveryType.equals("JD")) {
expressDelivery = new JDExpressDelivery();
} else if (deliveryType.equals("SF")) {
expressDelivery = new SFExpressDelivery();
} else {
expressDelivery = new YTExpressDelivery();
}
return expressDelivery;
}
}
客户端
java
public static void main(String[] args) {
String type = "SF";
OrderResponse orderResponse = createOrderByType(type);
System.out.println("快递单号为"+orderResponse.getBillNo());
}
public static OrderResponse createOrderByType(String type) {
IExpressDelivery expressDelivery = ExpressDeliveryFactory.create(type);
OrderRequest orderRequest = new OrderRequest();
return expressDelivery.createOrder(orderRequest);
}
3.3 简单工厂模式 + 反射优化
快递枚举类
java
@Getter
@AllArgsConstructor
public enum ExpressTypeEnum {
JD( "JD", "com.example.demo.express.JDExpressDelivery"),
SF("SF", "com.example.demo.express.SFExpressDelivery"),
YT("YT", "com.example.demo.express.YTExpressDelivery"),
;
private final String key;
private final String classPath;
}
优化后的工厂
java
public class ExpressDeliveryFactory {
public static IExpressDelivery create(ExpressTypeEnum expressTypeEnum) {
try {
Class<?> clazz = Class.forName(expressTypeEnum.getClassPath());
return (IExpressDelivery) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
客户端
java
public class Test {
public static void main(String[] args) {
OrderResponse orderResponse = createOrderByType(ExpressTypeEnum.SF);
System.out.println("快递单号为"+orderResponse.getBillNo());
}
public static OrderResponse createOrderByType(ExpressTypeEnum type) {
IExpressDelivery expressDelivery = ExpressDeliveryFactory.create(type);
OrderRequest orderRequest = new OrderRequest();
return expressDelivery.createOrder(orderRequest);
}
}
3.4 简单工厂模式的主要优点:
- 将对象的创建和具体产品的实现分离,降低了客户端代码与具体产品类的耦合度。
- 简化了客户端代码,客户端不需要了解对象的创建细节,只需调用工厂类的方法即可获得所需的对象。
- 可以轻松切换或替换具体产品类,不影响客户端的代码。
3.5 简单工厂模式一些局限性:
- 当需要添加新的产品类型时,需要修改工厂类的代码,违反了开闭原则。
- 工厂类可能会变得相当庞大,难以维护。
4. 工厂方法模式
4.1 请求快递实例
将工厂创建产品的方法提出来创建一个抽象类,把原来的工厂变成抽象工厂
java
public interface IExpressFactory {
IExpressDelivery create();
}
创建多个工厂
京东快递的工厂
java
public class JDExpressFactory implements IExpressFactory{
@Override
public IExpressDelivery create() {
return new JDExpressDelivery();
}
}
顺丰快递的工厂
java
public class SFExpressFactory implements IExpressFactory {
@Override
public IExpressDelivery create() {
return new SFExpressDelivery();
}
}
圆通快递的工厂
java
public class YTExpressFactory implements IExpressFactory {
@Override
public IExpressDelivery create() {
return new YTExpressDelivery();
}
}
工厂类的简单工厂
先建立枚举类
java
@Getter
@AllArgsConstructor
public enum ExpressFactoryTypeEnum {
JD( "JD", "com.example.demo.express.JDExpressFactory"),
SF("SF", "com.example.demo.express.SFExpressFactory"),
YT("YT", "com.example.demo.express.YTExpressFactory"),
;
private final String key;
private final String classPath;
}
工厂的工厂
java
public class ExpressFactoryFactory {
public static IExpressFactory create(ExpressFactoryTypeEnum expressFactoryTypeEnum) {
try {
Class<?> clazz = Class.forName(expressFactoryTypeEnum.getClassPath());
return (IExpressFactory) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
最后在客户端调用
java
public class Test1 {
public static void main(String[] args) {
OrderResponse orderResponse = createOrderByType(ExpressFactoryTypeEnum.JD);
System.out.println("快递单号为"+orderResponse.getBillNo());
}
private static OrderResponse createOrderByType(ExpressFactoryTypeEnum typeEnum) {
IExpressFactory iExpressFactory = ExpressFactoryFactory.create(typeEnum);
IExpressDelivery expressDelivery = iExpressFactory.create();
OrderRequest orderRequest = new OrderRequest();
return expressDelivery.createOrder(orderRequest);
}
}
4.2 工厂方法模式总结
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
4.3工厂方法模式的主要优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合"开闭原则"。
4.4 工厂方法模式的主要缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
4.5 适用场景
- 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
- 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。