1. 初衷

写这篇文章的初衷,介绍一下工厂形式,以及详细运用实例。

2. 简略介绍

首要清晰一点就是工厂是用来干啥的,工厂是用来创立目标的。
工厂形式的作用

  1. 封装目标的创立进程: 工厂形式将目标的创立进程封装在工厂类中,使客户端代码不需求了解详细的构建进程,只需向工厂恳求目标即可。
  2. 进步代码的可保护性: 工厂形式能够会集办理目标的创立,假如需求修正目标的构建方式,只需修正工厂类而不影响客户端代码。
  3. 进步代码的扩展性: 工厂形式答应增加新的产品类型而无需修正现有客户端代码。这样能够方便地扩展应用程序。
  4. 下降耦合度: 运用工厂形式能够下降客户端代码与详细产品类之间的耦合度,因为客户端只与工厂接口交互,不需求直接与产品类交互。
  5. 进步代码的重用性: 工厂形式能够在不同的地方运用相同的工厂来创立目标,进步了代码的重用性。

3. 简略工厂形式

3.1 恳求快递实例,无设计形式情况

公共快递下单接口

public interface IExpressDelivery {
    /**
     * 快递下单
     */
    OrderResponse createOrder(OrderRequest orderRequest);
}

京东快递下单

public class JDExpressDelivery implements IExpressDelivery{
    @Override
    public OrderResponse createOrder(OrderRequest orderRequest) {
        // 省掉恳求京东的下单api进程
        OrderResponse orderResponse = new OrderResponse();
        orderResponse.setBillNo("京东快递单号");
        return orderResponse;
    }
}

顺丰快递下单

public class SFExpressDelivery implements IExpressDelivery {
    @Override
    public OrderResponse createOrder(OrderRequest orderRequest) {
        // 省掉恳求顺丰的下单api进程
        OrderResponse orderResponse = new OrderResponse();
        orderResponse.setBillNo("顺丰快递单号");
        return orderResponse;
    }
}

圆通快递下单

public class YTExpressDelivery implements IExpressDelivery {
    @Override
    public OrderResponse createOrder(OrderRequest orderRequest) {
        // 省掉恳求圆通的下单api进程
        OrderResponse orderResponse = new OrderResponse();
        orderResponse.setBillNo("圆通快递单号");
        return orderResponse;
    }
}

客户端调用

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 恳求快递实例,运用简略工厂形式

工厂

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;
    }
}

客户端

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 简略工厂形式 + 反射优化

快递枚举类

@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;
}

优化后的工厂

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);
        }
    }
}

客户端

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 恳求快递实例

将工厂创立产品的办法提出来创立一个笼统类,把原来的工厂变成笼统工厂

public interface IExpressFactory {
    IExpressDelivery create();
}

创立多个工厂
京东快递的工厂

public class JDExpressFactory implements IExpressFactory{
    @Override
    public IExpressDelivery create() {
        return new JDExpressDelivery();
    }
}

顺丰快递的工厂

public class SFExpressFactory implements IExpressFactory {
    @Override
    public IExpressDelivery create() {
        return new SFExpressDelivery();
    }
}

圆通快递的工厂

public class YTExpressFactory implements IExpressFactory {
    @Override
    public IExpressDelivery create() {
        return new YTExpressDelivery();
    }
}

工厂类的简略工厂

先树立枚举类

@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;
}

工厂的工厂

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);
        }
    }
}

最终在客户端调用

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 适用场景

  • 客户端不知道它所需求的目标的类。在工厂办法形式中,客户端不需求知道详细产品类的类名,只需求知道所对应的工厂即可,详细的产品目标由详细工厂类创立,可将详细工厂类的类名存储在配置文件或数据库中。
  • 笼统工厂类通过其子类来指定创立哪个目标。在工厂办法形式中,对于笼统工厂类只需求供给一个创立产品的接口,而由其子类来确认详细要创立的目标,利用面向目标的多态性和里氏代换准则,在程序运行时,子类目标将覆盖父类目标,从而使得体系更简单扩展。