zoukankan      html  css  js  c++  java
  • 设计模式(二 & 三)工厂模式:1-简单工厂模式

    • 模拟场景:

      需要构造一个运算器(Operation),分别负责加减乘除的运算功能。

    • 思想:

      这里需要构造四个 Operation,可以使用 Factory 去统一创建这四个对象。

      所需要构造的对象是运算器(Operation),因为没有具体功能的运算器,是毫无作用的,所以可以将其定义为抽象类(Abstract Class)。

      需要有具体意义的构造器:加法运算器(OperationAdd),减法运算器(OperationSub),乘法运算器(OperationMul),除法运算器(OperationDiv)。

      根据工厂模式的思想,将这些构造器的创建工作,统一的管理起来,用一个工厂(OperationFactory)去创建。

    • UML:

       

    • UML 分析:

      以上是一个简化版(只有加法和减法)的运算器工厂模式 UML 图。

      OperationFactory 的 createOperation()  负责管理所有 Operation 的创建,根据入参(在 UML 中未标出)的不同选择不同运算器的初始化。

    • createOperation() 方法需要什么样的入参?

      一般推荐自定义一个枚举类型 enum,而不是使用字符串作为方法的入参。

      使用枚举类型的优势在于,避免客户端调用时有超出预计的输入。 

    • 代码:
    public abstract class AbsOperation {
    
        public abstract BigDecimal calc(double a, double b);
    }
    public final class OperationFactory1 {
    
        private OperationFactory1() {
    
        }
    
        public static AbsOperation createOperation(OperatorEnum operator) {
            switch (operator) {
                case OPERATOR_ADD:
                    return new OperationAdd();
                case OPERATOR_SUB:
                    return new OperationSub();
                case OPERATOR_MUL:
                    return new OperationMul();
                case OPERATOR_DIV:
                    return new OperationDiv();
                default:
                    throw new ArithmeticException("Undefined operation");
            }
        }
    }
    public final class OperationAdd extends AbsOperation {
    
        OperationAdd() {
    
        }
    
        @Override
        public BigDecimal calc(double a, double b) {
            return new BigDecimal(a).add(new BigDecimal(b));
        }
    }
    public enum OperatorEnum {
    
        OPERATOR_ADD, OPERATOR_SUB, OPERATOR_MUL, OPERATOR_DIV
    }
    • 为什么在具体的 Operation 类的构造器要加上包级别限制?

       一旦我们将一个类的构造过程纳入工厂的管理,那么我们就不希望在其他地方可以通过 new 的方式来创建对象,所以需要对构造器加上访问权限的控制。

    • 有什么可以进一步优化的地方?
    1. 返回类型。createOperation() 方法的返回类型是 AbsOperation,但是对于调用者来说,我是明确知道我是需要什么样的 Operation 的,例如 OperationAdd。那么如果调用方希望使用一些 OperationAdd 独有的方法,而不是 AbsOperation 的方法,那么还需要对返回类型做一次强转。
    2. 对于扩展的不便。一旦我们需要更多的 Operation,想象一下需要如何修改代码?
      1. 首先,需要增加一个 Operation 类 extends AbsOperation。
      2. 其次,OperationEnum 需要增加一个枚举类型。
      3. 最后,OperationFactory 的 createOperaion 需要增加一层 switch-case 结构。 
    3. 对 createOperation() 方法的扩展,不但会使代码变得更加臃肿,还违反了 OO 设计原则中的“开闭原则”,即:对扩展开放,对修改关闭。  
    • 可以怎么改进? 

      针对上面提出的 Easy Factory 的劣势,有两个解决方案:

    1. 使用反射和泛型来优化工厂。
    2. 使用 Factory Method,详情见:设计模式(二)工厂模式:2-工厂方法模式
    • 反射和泛型:

      这个的核心思想是,调用方提供需要创建构造器的具体的 Class 对象,利用反射简化 createOperation() 方法。

    public final class OperationFactory2 {
    
        private OperationFactory2() {
    
        }
    
        public static <T extends AbsOperation> T createOperation(Class<T> operationClass) {
            try {
                return operationClass.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("Something wrong when create " + operationClass.getName() + " instance");
            }
        }
    }
    • Operation 还有什么特点?

      Operation 这个对象,比较特殊。

      我们随便看一个其他的工厂,例如创建订单的工厂 OrderFactory,它创建的每一个 Order 都是有意义的。

      而 Operation 这个东西,我们并不需要多个运算器,对于每一种运算器,它们都应该具有 Singleton 的属性。

      这种实现 Singleton 的方式,可以参照:设计模式(一)单例模式:6-登记模式,在 Factory 中增加一个 Registry。

    public final class OperationFactory3 {
    
        private OperationFactory3() {
    
        }
    
        private static final Map<Class<?>, AbsOperation> operMap = new HashMap<>();
    
        @SuppressWarnings("unchecked")
        public static <T extends AbsOperation> T createOperation(Class<T> operationClass) {
            try {
                T t;
                if (operMap.containsKey(operationClass)) {
                    t = (T) operMap.get(operationClass);
                } else {
                    t = operationClass.newInstance();
                    operMap.put(operationClass, t);
                }
                return t;
            } catch (Exception e) {
                throw new RuntimeException("Something wrong when create " + operationClass.getName() + " instance");
            }
        }
    }
    • 静态工厂

      工厂提供的方法是 static 的,所以又称为静态工厂方法。

      《Effective Java》 第1条:考虑用静态工厂方法代替构造器

      静态工厂方法,相比于构造器有4个优势:

    1. 有方法签名:避免了多参数构造器意义不明的情况。
    2. 不必再每次调用的时候创建新的对象:在必要时可以做单例控制。
    3. 可以返回原返回类型的任意子类型:增加代码的灵活性。
    4. 在创建参数化类型实例的时候,使代码更加简洁:这是在 JDK1.6 之前的优势。
  • 相关阅读:
    微信扫码登陆
    jquery的js代码兼容全部浏览器的解决方法
    如何才能成为一名优秀的架构师
    Bootstrap 完全教程笔记
    vue.js笔记总结
    dot.js模板实现分离式
    python基础知识1
    tensorflow基础知识1
    tensorflow基础知识
    常用python库文件
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/7521771.html
Copyright © 2011-2022 走看看