• 模拟场景:

  需要构造一个运算器(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 之前的优势。

设计模式(二 & 三)工厂模式:1-简单工厂模式的更多相关文章

  1. 设计模式(Java版)-创建型模式之简单工厂模式

    前言:这段时间在学习设计模式,本人也是小菜一枚(所以写的如果有错误的地方请大大们给予指出).这个东西也是我一直想学习的,从点点滴滴做起,记录下自己每天的领悟! 一.工厂模式的动机 在软件系统中,经常面 ...

  2. 大话设计模式C++实现-第1章-简单工厂模式

    一.UML图 二.包括的角色 简单工厂模式包括三个角色: (1)工厂类Factory:工厂类是用来制造产品的. 因此,在Factory中有一个用于制造产品的Create函数或者Generate函数之类 ...

  3. Java设计模式---工厂模式(简单工厂、工厂方法、抽象工厂)

    工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...

  4. 设计模式在cocos2d-x中的使用--简单工厂模式(Simple Factory)

    什么是简单工厂模式? 从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式.通过专门定义一个类来负责创建其它类的实例,被创建的实例 ...

  5. Java设计模式之(工厂模式)--简单工厂模式--工厂方法模式--抽象工厂模式

    工厂模式: 工厂模式可以分为三类: 1)简单工厂模式(Simple Factory) 2)工厂方法模式(Factory Method) 3)抽象工厂模式(Abstract Factory) 简单工厂模 ...

  6. 设计模式之策略模式&amp;简单工厂模式

    学习设计模式已经有非常长一段时间了,事实上先前已经敲过一遍了.可是老认为没有学到什么,认识也不够深刻.如今趁着重构机房,再又一次来过,也不晚. 事实上在敲了机房之后,看看模式,事实上,曾经非常难理解. ...

  7. Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---工厂模式之简单工厂

    简单工厂:工厂依据传进的参数创建相应的产品. http://www.cnblogs.com/DelphiDesignPatterns/archive/2009/07/24/1530536.html { ...

  8. 设计模式之工厂方法模式VS简单工厂方法模式

    名词解释: 简单工厂:这个实在是没什么解释的,就是一个工厂类,然后有一个方法,根据传递的参数可以通过switch(你也可以是if,或者是使用高端的反射 )来进行对象的创建. 工厂方法:定义一个用于创建 ...

  9. C++设计模式 ==> 策略模式与简单工厂模式结合

    简介 策略模式相较之于简单工厂模式适用于生产方法经常变化且方法较为繁多的情况,因为生产方法时常变化就会需要频繁修改工厂类,违背了开闭原则,这时就可以用策略选择类由客户端根据需求动态切换策略.且策略模式 ...

  10. 设计模式之工厂模式之简单工厂(php实现)

    github源码地址: git@github.com:ZQCard/design_pattern.git 1.简单工厂模式 特点:将调用者与创建者分离,调用者直接向工厂请求,减少代码的耦合.提高系统的 ...

随机推荐

  1. BZOJ 2654: tree Kruskal+二分答案

    2654: tree Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1863  Solved: 736[Submit][Status][Discuss ...

  2. [VC]在VC++中实现让程序只运行一个实例的方法且实现该实例

    方法一: 有时候在开发应用程序时,希望控制程序运行唯一的实例.例如,最常用的mp3播放软 件Winamp,由于它需要独占计算机中的音频设备,因此该程序只允许自身运行唯一的一个例程.在Visual C+ ...

  3. python_88_xml模块

    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单例如创建xmltest.xml文件内容如上 注:/代表自结束符号 <?xml version=&quo ...

  4. 多线程编程之pthread线程深入理解

    不同的平台和操作系统上 进程和线程的实现机制不完全一致  但是一般来说线程栈都是独立的 只要得到地址就可以相互访问       Pthread是 POSIX threads 的简称,是POSIX的线程 ...

  5. LiteIDE 错误: 进程无法启动

    问题 运行 01_hello.go,提示以下错误 新建文件夹().exe [C:/Users/Administrator/Desktop/新建文件夹()] 错误: 进程无法启动. 原因 工程目录名不能 ...

  6. shell脚本,awk取中间列的方法。

    解释 1.$(int(NF/2)+1) 中int(NF/2)等于3,然后加1,就得到中间的4了. 2.$(NF/2+0.5) 相当于得出的是整数.NF/2是3.5,再由3.5+0.5,所以就是4了,也 ...

  7. webservice基础

    一.webservice概念 webservice用于异构平台之间的交互,我用Java写的程序,可以用php..net.pythod等其它语言的程序来访问我的接口.webservice有很多框架帮我们 ...

  8. 01_2_Namespace命名空间

    01_2_Namespace命名空间 1. Namespace_命名空间 namespace决定了action的访问路径,默认为””,可以接收所有路径的action namespace可以写为/,或者 ...

  9. cocos2dx通过ndk编译c++库

    ndk编译c++库,然后通过jni调用实现重要代码封装,是安卓应用中最常用的技术,一方面可以将重要的代码实现隐藏,防止泄漏,也可以提高打包速度. ndk里面的sample文件夹中有很多实用的例子,其中 ...

  10. MySQL 如何将Id相同的字段合并,并且以逗号隔开

    数据库存的数据 sql: SELECT Id,GROUP_CONCAT(`Name` SEPARATOR ',') NAMES FROM `stu` GROUP BY Id;