工厂方法模式

定义创建对象的接口(或抽象类),让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类进行。

分析实现步骤

类图
image.png

  1. 创建抽象类(不变)
  2. 创建地域特色的产品类
  3. 创建工厂方法
  4. 创建子类实现工厂方法
  5. 客户端测试
  6. 分析

代码实现

创建地域特色的产品类

  1. /**
  2. * 中国奶油蛋糕
  3. */
  4. public class CNCreamCake extends AbstractCake {
  5. @Override
  6. public void prepare() {
  7. System.out.println("原料:原胚、奶油、中国元素");
  8. }
  9. }
  1. /**
  2. * 中国水果蛋糕
  3. */
  4. public class CNfruitCake extends AbstractCake {
  5. @Override
  6. public void prepare() {
  7. System.out.println("原料:原胚、水果、中国元素");
  8. }
  9. }
  1. /**
  2. * 美国奶油蛋糕
  3. */
  4. public class USCreamCake extends AbstractCake {
  5. @Override
  6. public void prepare() {
  7. System.out.println("原料:原胚、奶油、美国元素");
  8. }
  9. }
  1. /**
  2. * 美国水果蛋糕
  3. */
  4. public class USfruitCake extends AbstractCake {
  5. @Override
  6. public void prepare() {
  7. System.out.println("原料:原胚、水果、美国元素");
  8. }
  9. }

创建工厂方法

  1. /**
  2. * 工厂方法,有方法,但不实现,下放到子类去执行
  3. */
  4. public abstract class FactoryMethod {
  5. //定义一个抽象方法,让各个工厂子类自己实现
  6. abstract AbstractCake createCake(String orderType);
  7. public FactoryMethod() {
  8. AbstractCake cake;
  9. String orderType;
  10. do {
  11. orderType = getType();
  12. //获取简单(静态)工厂
  13. cake = createCake(orderType);
  14. if(cake != null) {
  15. cake.prepare();
  16. cake.bake();
  17. cake.box();
  18. cake.send();
  19. } else {
  20. System.out.println("预定失败");
  21. break;
  22. }
  23. }while(true);
  24. }
  25. //输入蛋糕类型
  26. private String getType() {
  27. try {
  28. BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
  29. System.out.println("请输入蛋糕类型:");
  30. return strin.readLine();
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. return "";
  34. }
  35. }
  36. }

创建子类实现工厂方法

  1. /**
  2. * 子类实现(中国)工厂方法
  3. */
  4. public class CNOrderCake extends FactoryMethod{
  5. @Override
  6. AbstractCake createCake(String orderType) {
  7. AbstractCake cake = null;
  8. if (orderType.equals("水果")) {
  9. cake = new CNfruitCake();
  10. cake.setName(orderType + "蛋糕(中国味道)");
  11. } else if (orderType.equals("奶油")) {
  12. cake = new CNCreamCake();
  13. cake.setName(orderType + "蛋糕(中国味道)");
  14. } else {
  15. System.out.println("暂不支持的蛋糕类型");
  16. return cake;
  17. }
  18. return cake;
  19. }
  20. }
  1. /**
  2. * 子类实现(美国)工厂方法
  3. */
  4. public class USOrderCake extends FactoryMethod{
  5. @Override
  6. AbstractCake createCake(String orderType) {
  7. AbstractCake cake = null;
  8. if (orderType.equals("水果")) {
  9. cake = new USfruitCake();
  10. cake.setName(orderType + "蛋糕(美国味道)");
  11. } else if (orderType.equals("奶油")) {
  12. cake = new USCreamCake();
  13. cake.setName(orderType + "蛋糕(美国味道)");
  14. } else {
  15. System.out.println("暂不支持的蛋糕类型");
  16. return cake;
  17. }
  18. return cake;
  19. }
  20. }

客户端测试

  1. public class CakeStore {
  2. public static void main(String[] args) {
  3. //中国
  4. new CNOrderCake();
  5. //美国
  6. // new USOrderCake();
  7. }
  8. }
  9. //测试结果
  10. 请输入蛋糕类型:
  11. 水果
  12. 原料:原胚、水果、中国元素
  13. 水果蛋糕(中国味道)烘焙;
  14. 水果蛋糕(中国味道)包装;
  15. 水果蛋糕(中国味道)配送;
  16. 请输入蛋糕类型:
  17. 巧克力
  18. 暂不支持的蛋糕类型
  19. 预定失败

分析
由上代码可知,此时如果新增一个英国口味的各种蛋糕,则继承通用工厂方法,新增一个定制的子类预定类的实现,以及新增对应的类型的蛋糕实现。确实有很好的OCP,但是有没有觉得很复杂,新建的类有点多,感觉还不如简单方法细化蛋糕类(TODO 可能境界没有达到,后面再体会一下)。

框架或项目源码分析


应用场景

  • 客户只知道创建产品的工厂名,而不知道具体的产品名
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口
  • 客户不关心创建产品的细节,只关心产品的品牌