简说设计模式——工厂方法模式
一、什么是工厂方法模式
工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式使一个类的实例化延迟到其子类。UML结构图如下:
其中,Product定义工厂方法所创建的对象的接口;Creator声明工厂方法,并返回一个Product类型的对象;ConcreteProduct是具体的产品,实现了Product接口;ConcreteCreteCreator重定义工厂方法,返回一个ConcreteProduct实例。
1. Product类
下述代码是一个抽象产品类,具体的产品类可以有多个,都继承于抽象产品类。1 public abstract class Product { 2 //产品类的公共方法 3 public void method1() { 4 //业务逻辑处理 5 } 6 //抽象方法 7 public abstract void method2(); 8 }
2. ConcreteProduct类
具体产品类,可创建多个。
1 public class ConcreteProduct1 extends Product { 2 3 @Override 4 public void method2() { 5 // 业务逻辑处理 6 } 7 8 }
3. Creator类
下述代码是一个抽象工厂类,主要负责定义产品对象的产生。
1 public abstract class Creator { 2 3 //创建一个产品对象,参数自行设置 4 public abstract <T extends Product> T createProduct(Class<T> c); 5 6 }
4. ConcreteCreator类
具体如何产生一个产品的对象,是由具体的工厂类实现的。
1 public class ConcreteCreator extends Creator { 2 3 @Override 4 public <T extends Product> T createProduct(Class<T> c) { 5 Product product = null; 6 try { 7 product = (Product) Class.forName(c.getName()).newInstance(); 8 } catch (Exception e) { 9 // TODO: handle exception 10 } 11 return (T) product; 12 } 13 14 }
调用时就可以使用我们创建的工厂来完成相应的操作了,如下:
1 Creator creator = new ConcreteCreator(); 2 creator.createProduct(ConcreteProduct1.class);
二、工厂方法模式的应用
1. 何时使用
- 不同条件下创建不用实例时。方法是让子类实现工厂接口。
2. 优点
- 良好的封装性,代码结构清晰。如一个调用者想创建一个对象,只需要知道其名称即可,降低了模块间的耦合。
- 扩展性好。如果想增加一个产品,只需扩展一个工厂类即可。
- 屏蔽产品类。调用者只关心产品的接口。
- 典型的解耦框架。
3. 缺点
- 每增加一个产品,就需要增加一个产品工厂的类,增加了系统的复杂度。
4. 使用场景
- 需要生成对象的地方。
- 需要灵活的、可扩展的框架时。
- 数据库访问,数据库可能变化时。
5. 应用实例
- 需要一辆汽车,直接从工厂里面提货,不用去管这辆车是怎么做出来的。
- Hibernate换数据库只需换方言和驱动即可。
- 简单计算器的实现。
三、简单工厂模式的实现
在看工厂方法模式的实现之前,我们先来了解一下简单工厂模式。简单工厂模式就是用一个单独的类来做创造实例的过程,这个类就是工厂。
我们以简单计算器的实现为例,这里只给出部分代码用于与工厂方法模式做对比。UML图如下:
工厂类如下:
1 public class OperationFactory { 2 public static Operation createOperate(String operate) { 3 Operation oper = null; 4 switch(operate) { 5 case "+": 6 oper = new OperationAdd(); 7 break; 8 case "-": 9 oper = new OperationSub(); 10 break; 11 case "*": 12 oper = new OperationMul(); 13 break; 14 case "/": 15 oper = new OperationDiv(); 16 break; 17 } 18 return oper; 19 } 20 }
其余类参考下方工厂方法模式。
四、工厂方法模式的实现
现在再对这个计算器用工厂方法模式进行编写,看一下两种模式间有什么区别。UML图如下:
1. 运算类
1 public class Operation { 2 3 protected double numberA = 0; 4 protected double numberB = 0; 5 6 public double getNumberA() { 7 return numberA; 8 } 9 public void setNumberA(double numberA) { 10 this.numberA = numberA; 11 } 12 public double getNumberB() { 13 return numberB; 14 } 15 public void setNumberB(double numberB) { 16 this.numberB = numberB; 17 } 18 19 public double getResult() { 20 double result = 0; 21 return result; 22 } 23 }
2. 工厂接口
1 public interface IFactory { 2 Operation createOperation(); 3 }
3. 具体运算类
这里以加减乘除四种运算为例,需要四个实现类,都继承运算类。
1 public class OperationAdd extends Operation { 2 3 @Override 4 public double getResult() { 5 double result = 0; 6 result = numberA + numberB; 7 8 return result; 9 } 10 11 }
其余三个省略。
4. 运算工厂
有四个运算类,就需要四个运算工厂,都实现了工厂接口。1 public class AddFactory implements IFactory { 2 3 @Override 4 public Operation createOperation() { 5 return new OperationAdd(); 6 } 7 8 }
5. Client客户端
1 public class Client { 2 3 public static void main(String[] args) { 4 IFactory oFactory = new AddFactory(); 5 // IFactory oFactory = new SubFactory(); 6 // IFactory oFactory = new MulFactory(); 7 // IFactory oFactory = new DivFactory(); 8 9 Operation operation = oFactory.createOperation(); 10 11 operation.numberA = 5; 12 operation.numberB = 7; 13 14 double result = operation.getResult(); 15 System.out.println(result); 16 } 17 18 }
如上述代码,为加法的运算,若需要进行其他运算,只需实现该接口的其他实现类(如注释所示)。运行结果如下:
实现加法工厂,进行加法运算,5+7的结果为12。
五、简单工厂方法与工厂方法模式的区别
如果现在需要增加其他运算,比如取余。简单工厂模式需要在添加case分支条件,修改了原有的类,违背了开闭原则;而工厂方法模式只需再新加个取余类和取余工厂,然后对客户端进行修改即可。
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对与客户端来说,去除了与具体产品的依赖。为了弥补他违背了开闭原则,于是就有了工厂方法模式,根据依赖倒转原则,把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。
其实工厂方法模式还存在一个问题,就是客户端需要决定实例化哪一个工厂来实现运算类,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。对于这个问题,可以利用反射来解决(具体实例可参考抽象工厂模式中的反射实例)。