模拟场景:
现在有如下需求,需要构造一个运算器(Operation)去实现加减乘除的功能,怎么设计?
思想:
所需要构造的对象是运算器(Operation),因为没有具体功能的运算器,是毫无作用的,所以可以将其定义为抽象类(Abstract Class)。
需要有具体意义的构造器:加法运算器(OperationAdd),减法运算器(OperationSub),乘法运算器(OperationMul),除法运算器(OperationDiv)。
根据工厂模式的思想,将这些构造器的创建工作,统一的管理起来,用一个工厂(OperationFactory)去创建。
UML:
分析:
以上是一个简化版(只有加法和减法)的运算器工厂模式 UML 图。
通过包级别的代码控制,使所有运算器类的构造器,不被外部获得,只能通过 OperationFactory 的 createOperation() 方法完成。
OperationFactory 的 createOperation() 负责管理所有类的创建,根据入参(在 UML 中未标出)的不同选择不同运算器的初始化。
代码:
1 public abstract class AbsOperation { 2 3 abstract double calc(double... vals); 4 5 }
1 public final class OperationAdd extends AbsOperation { 2 3 OperationAdd() { 4 5 } 6 7 @Override 8 public double calc(double... vals) { 9 double sum = 0; 10 for (double num : vals) { 11 sum += num; 12 } 13 return sum; 14 } 15 16 }
1 public enum OperatorEnum { 2 3 OPERATOR_ADD, OPERATOR_SUB, OPERATOR_MUL, OPERATOR_DIV 4 5 }
1 public final class OperationFactory { 2 3 public static AbsOperation createOperation(OperatorEnum operator) { 4 switch (operator) { 5 case OPERATOR_ADD: 6 return new OperationAdd(); 7 case OPERATOR_SUB: 8 return new OperationSub(); 9 case OPERATOR_MUL: 10 return new OperationMul(); 11 case OPERATOR_DIV: 12 return new OperationDiv(); 13 default: 14 throw new ArithmeticException("未定义的运算类型"); 15 } 16 } 17 18 }
反射优化:
通过以上代码分析,工厂根据传入的枚举值,决定到底初始化哪一个运算器类,这么做有2个缺点:
- 返回类型,是定义的抽象类,如果需要调用子类的个性化方法,需要在外部代码强转。
- 需要多维护一个枚举类,如果运算器的种类很多,枚举类会变得难以维护,而且 createOperation() 方法会变得很膀肿。
可以通过反射的方式,将 Class 对象作为 createOperation() 方法的入参,利用泛型的特点,避免以上这2个缺点。
1 public final class OperationFactory { 2 3 public static <T extends AbsOperation> T createOperation(Class<T> operationClass) { 4 try { 5 return operationClass.newInstance(); 6 } catch (InstantiationException | IllegalAccessException e) { 7 throw new ArithmeticException("未定义的运算类型"); 8 } 9 } 10 11 }
考虑单例:
最后再多考虑一件事情,运算器这个东西,比较特殊。
在整个系统中,并不需要多个运算器对象,也就是说这应该被认为是一个单例的对象。那么,工厂负责管理对象,也需要考虑单例的情况。
显然,在各个运算器对象中,增加代码来控制单例显然并不现实,那么类似之前说过的第6种单例实现:登记模式,就是一个很好地思想。
类比而言,Factory 起的作用,和登记模式中的 Registry,差别不大。
1 public final class OperationFactory { 2 3 private static final Map<Class<?>, AbsOperation> operMap = new HashMap<>(); 4 5 @SuppressWarnings("unchecked") 6 public static <T extends AbsOperation> T createOperation3(Class<T> operationClass) { 7 try { 8 T t; 9 if (operMap.containsKey(operationClass)) { 10 t = (T) operMap.get(operationClass); 11 } else { 12 t = operationClass.newInstance(); 13 operMap.put(operationClass, t); 14 } 15 return t; 16 } catch (InstantiationException | IllegalAccessException e) { 17 throw new ArithmeticException("未定义的运算类型"); 18 } 19 } 20 21 }
通过存储一个 Map,将已经创建过的对象,存储在 Map 中,在第二次调用方法时,直接从 Map 中获取对象,从而达到单例的目的。
总结:
- 简单工厂模式,由于其工厂对象是独立的,一般都是将其中创建对象的方法设置为 static,避免创建工厂对象,所以也被称为静态工厂模式(Static Factory Method).
- 由于方法是静态的,导致在实现单例时,代码有点不舒服,就是在需要将 Map 设计为 Class-AbsOperation 结构,导致在具体实现中,需要强转。
- 简单工厂模式最大的缺点在于,面对新增的类,例如阶乘运算器,不但需要新增一个运算器类,还要对 createOperation() 方法做出改变,这就违反了开闭原则。
- 这一点,在之后的工厂方法模式(Factory Method)中给出了解决方案。