什么是工厂方法模式?

工厂方法模式(Factory method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

还记得在第3章的简单工厂模式,我们实现了一个简易计算器。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,去除了与具体运算类的依赖

但其问题也就在这里,如果要加一个‘求余’运算符的功能,我们需要在运算工厂类的方法里加‘Case’的分支条件的,修改原有的类?这就等于说,我们不但对扩展开放了,对修改也开放了,就违背了开放-封闭原则

那如何用工厂方法模式来实现呢?其UML类图如下。
image.png

工厂方法实现

我们来看看如果使用工厂方法,那么之前的简易计算器应该如何实现。计算器的要求如下:
“实现一个计算器程序,要求输入两个数和运算符号,得到结果,暂时实现加减乘除即可,考虑之后的运算符扩充。”

基于工厂方法,其UML类图如下:
image.png
运算符类
运算符类跟第3节相同:

  1. package com.whitedew.factorymethod;
  2. //操作类
  3. public class Operation {
  4. //子类能继承父类的所有属性,但父类若为私有属性,子类只是拥有,无法使用。
  5. //因此使用protected修饰
  6. protected double numberA = 0;
  7. protected double numberB = 0;
  8. public double getNumberA() {
  9. return numberA;
  10. }
  11. public void setNumberA(double numberA) {
  12. this.numberA = numberA;
  13. }
  14. public double getNumberB() {
  15. return numberB;
  16. }
  17. public void setNumberB(double numberB) {
  18. this.numberB = numberB;
  19. }
  20. public double getResult() {
  21. double result = 0;
  22. return result;
  23. }
  24. }

具体运算类
同第3节的代码,暂略。

工厂接口
所有的具体运算类都需要一个工厂来实现这个接口。

  1. public interface IFactory {
  2. Operation createOperation();
  3. }

运算类的工厂
所有的运算类都需要一个对应的工厂,来实现上面的那个工厂接口,并且由这个工厂实例来创建不同的产品实例。以乘法为例:

  1. public class MultiFactory implements IFactory {
  2. @Override
  3. public Operation createOperation() {
  4. return new OperationMulti();
  5. }
  6. }

计算器的客户端
由于switch case已经从工厂类中移除,于是有关运算符的判断就需要放到客户端来实现了。

  1. package com.whitedew.factorymethod;
  2. public class CalculatorClient {
  3. public static void main(String[] args) {
  4. IFactory iFactory = null;
  5. int numberA = 0;
  6. int numberB = 0;
  7. String operationStr = null;
  8. Scanner scanner = new Scanner(System.in);
  9. System.out.print("请输入数字A : ");
  10. // 判断是否还有输入
  11. if (scanner.hasNext()) {
  12. numberA = scanner.nextInt();
  13. System.out.println("输入的数据为:" + numberA);
  14. }
  15. System.out.print("请选择运算符(+-*/): ");
  16. // 判断是否还有输入
  17. if (scanner.hasNext()) {
  18. operationStr = scanner.next();
  19. System.out.println("选择的运算符为:" + operationStr);
  20. }
  21. System.out.print("请输入数字B : ");
  22. // 判断是否还有输入
  23. if (scanner.hasNext()) {
  24. numberB = scanner.nextInt();
  25. System.out.println("输入的数据为:" + numberB);
  26. }
  27. switch (operationStr) {
  28. case "+":
  29. iFactory = new AddFactory();
  30. break;
  31. case "-":
  32. iFactory = new SubFactory();
  33. break;
  34. case "*":
  35. iFactory = new MultiFactory();
  36. break;
  37. case "/":
  38. iFactory = new DivFactory();
  39. break;
  40. default:
  41. System.out.println("操作符为空");
  42. System.exit(0);
  43. }
  44. Operation operation = iFactory.createOperation();
  45. operation.setNumberA(numberA);
  46. operation.setNumberB(numberB);
  47. double result = operation.getResult();
  48. System.out.println("结果为" + numberA + operationStr + numberB + "=" + result);
  49. }
  50. }

结果
运算结果如下,3*5:
image.png

总结

从上面的例子可以看出,工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点(即违背开放-封闭原则)

但缺点是由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量,但是这个缺点在大部分时候都是可以接收的。同时,由于判断逻辑放在了客户端,增加了客户端的代码开发量,有关这一点,在Java语言中,可以通过抽象工厂模式+反射来实现,详见第5节抽象工厂模式。

参考资料

《大话设计模式》