工厂方法模式
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
了解过简单工厂模式的小伙伴都知道,简单工厂模式的一个致命缺点就是违背了开放封闭原则,不了解的可以看一下我之前对简单工厂模式的介绍。
以之前讲过的计算器类为例,简单工厂模式每增加一个运算方法就需要对简单工厂类进行修改,违背了开放封闭原则。
先来对比分析一下两个模式的UML图:
简单工厂模式:
工厂方法模式:
package caculator;
//运算器父类
public abstract class Operation {
private double _numberA=0;
private double _numberB=0;
public double get_numberA() {
return _numberA;
}
public void set_numberA(double _numberA) {
this._numberA = _numberA;
}
public double get_numberB() {
return _numberB;
}
public void set_numberB(double _numberB) {
this._numberB = _numberB;
}
public double GetResult(){
double result=0;
return result;
}
}
package caculator;
//运算器加法类
public class OperationAdd extends Operation {
@Override
public double GetResult(){
double result=0;
result = get_numberA()+get_numberB();
return result;
}
}
package caculator;
//运算器除法类
public class OperationDiv extends Operation {
@Override
public double GetResult(){
double result=0;
if(get_numberB()==0){
try {
throw new Exception("除数不能为0!");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
result = get_numberA()/get_numberB();
return result;
}
}
package caculator;
//运算器乘法类
public class OperationMul extends Operation {
@Override
public double GetResult(){
double result=0;
result = get_numberA()*get_numberB();
return result;
}
}
package caculator;
//运算器减法类
public class OperationSub extends Operation {
@Override
public double GetResult(){
double result=0;
result = get_numberA()-get_numberB();
return result;
}
}
package caculator;
//工厂接口
public interface IFactory{
Operation CreateOperation();
}
package caculator;
//加法工厂
class AddFactory implements IFactory{
public Operation CreateOperation(){
return new OperationAdd();
}
}
package caculator;
//减法工厂
class AddFactory implements IFactory{
public Operation CreateOperation(){
return new OperationSub();
}
}
package caculator;
//乘法工厂
class AddFactory implements IFactory{
public Operation CreateOperation(){
return new OperationMul();
}
}
package caculator;
//除法工厂
class AddFactory implements IFactory{
public Operation CreateOperation(){
return new OperationDiv();
}
}
package caculator;
public class client {
public static void main(String[] args){
IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
oper.set_numberA(1);
oper.set_numberB(2);
double result=oper.GetResult();
}
}
代码很容易实现,就是把简单工厂模式里面简单工厂类实例化具体算法的功能分散到了具体的工厂类中,但是我们看完代码会发现,当增加一个新的算法的时候,我们会增加相关的算法类和工厂类,这没问题,但是我们还要去更改客户端,这不就等于没简化难度,反而增加了很多类和方法,把复杂性增加了吗?
其实简单工厂模式最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖,但是如果要增加新的算法类,会违背开放封闭的原则。
工厂模式方法实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行,如果想要增加功能,本来是改工厂类的,而现在是修改客户端!
想再深入练习一下工厂方法模式的可以看一下下面的例题,不想的直接跳过例题看反射。
等下我们再看反射写法,现在我们先来练习一下工厂方法模式,来个例题:
在实际的企业应用中,一个公司的系统往往分散在很多不同的地方运行。公司既没有建立全公司专网的实力,又不愿意让业务数据实时地在广域网上传递。折中的方案是各分公司内运行系统独立,每天业务结束时,各分公司导出业务数据、打包、传送给总公司。
导出数据格式会有不同要求
文本格式、数据库备份格式、Excel格式、Xml格式
分清哪是产品类、哪是工厂类,使用工厂方法模式完成,只需模拟实现导出功能即可
例题答案:
UML图:
代码:
package Export;
//产品接口类
public interface ExportApi {
public boolean export(String data);
}
package Export;
//具体的产品类
public class ExportDB implements ExportApi {
public boolean export(String data){
System.out.println("导出数据"+data+"到数据库备份文件");
return true;
}
}
package Export;
//具体的产品类
public class ExportExcel implements ExportApi{
@Override
public boolean export(String data) {
System.out.println("导出数据"+data+"到Excel文件");
return true;
}
}
package Export;
//具体的产品类
public class ExportTxt implements ExportApi{
@Override
public boolean export(String data) {
System.out.println("导出数据"+data+"到文本文件");
return true;
}
}
package Export;
//具体的产品类
public class ExportXML implements ExportApi{
@Override
public boolean export(String data) {
System.out.println("导出数据"+data+"到XML文件");
return true;
}
}
package Export;
//抽象工厂方法类
public abstract class ExportFactory {
//用于被继承 在本类中没有作用
protected abstract ExportApi factoryMethod();
public boolean export(String data){
ExportApi api = factoryMethod();
return api.export(data);
}
}
package Export;
//具体的工厂方法类,用来实例化具体的产品类
public class ExportDBFactory extends ExportFactory{
@Override
protected ExportApi factoryMethod() {
return new ExportDB();
}
}
package Export;
//具体的工厂方法类,用来实例化具体的产品类
public class ExportExcelFactory extends ExportFactory{
@Override
protected ExportApi factoryMethod() {
return new ExportExcel();
}
}
package Export;
//具体的工厂方法类,用来实例化具体的产品类
public class ExportTxtFactory extends ExportFactory{
@Override
protected ExportApi factoryMethod() {
return new ExportTxt();
}
}
package Export;
//具体的工厂方法类,用来实例化具体的产品类
public class ExportXMLFactory extends ExportFactory{
@Override
protected ExportApi factoryMethod() {
return new ExportXML();
}
}
package Export;
//客户端
public class Client {
public static void main(String[] args) {
ExportFactory factory=new ExportDBFactory();
factory.export("测试数据");
}
}
例题结束,下面是工厂模式和反射的结合,
这里不讲反射,只运用反射
其实运用了反射之后,简单工厂模式和工厂方法模式的缺点就都解决了,但是根据单一职责原则可知,还是工厂方法模式更符合设计模式的基本原则。
我们根据反射来修改简单工厂类
package ch11.jdgc;
public class OperationFactory {
public static Operation createOperation(String operate) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Operation oper = null;
String className=Operation.class.getName()+operate;
//class.getName()获取的是该类的全类名ch11.jdgc.Operation
oper= (Operation) Class.forName(className).newInstance();
/*switch (operate) {
case "+":
oper=new OperationAdd();
break;
case "-":
oper=new OperationSub();
break;
case "/":
oper=new OperationDiv();
break;
case "*":
oper=new OperationMul();
break;
default:
break;
}*/
return oper;
}
}
可以直观的感受到代码变得简单了许多,省去了繁琐的switch case语句,要想调用不同的方法只需修改客户端即可
客户端代码:
package ch11.jdgc;
public class client {
public static void main(String[] args) {
Operation oper;
try {
oper=OperationFactory.createOperation("Mul");
oper.set_numberA(1.5);
oper.set_numberB(2);
double result = oper.GetResult();
System.out.println(result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
可是还是有小伙伴说了,虽然有用,但顶多跟工厂方法模式一样,虽然增加具体的算法类满足了开放封闭,可是如果要使用不用的算法类就需要修改客户端,还是改掉修改客户端的缺点啊。
虽然同理工厂方法模式也能运用反射来替代客户端的new对象实例,但是仍然不完全满足封闭。
那么反射+配置文件呢?
这里提供了两种配置文件的解析操作,分别是XML和Properties
XML解析方式(这里用的是javax.xml.parsers 包中的DOM模式的解析)
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fact.newDocumentBuilder();
String path = client.class.getClassLoader().getResource("caculator.xml").getPath();
Document doc = builder.parse(path);
NodeList add = doc.getElementsByTagName("caculator");
String op=add.item(0).getTextContent();
System.out.println(op);
对应的XML文件
<?xml version="1.0" encoding="utf-8" ?>
<caculators>
<caculator>Mul</caculator>
</caculators>
Properties解析方式
Properties pro = new Properties();
InputStream is = client.class.getClassLoader().getResourceAsStream("pro.properties");
pro.load(is);
String op=pro.getProperty("caculator");
System.out.println(op);
对应的Properties文件
caculator=Mul
反射+配置文件的方式完美的解决了不满足开放封闭的问题,要改算法类只需要修改配置文件即可,不会再有改变需求需要一并去改变类的情况发生了。~