从XX系统探究工厂模式
近期一直在查看XX系统,该系统服务于全国各个省市,审核流程全国统一规则,但是依然有其中几个省采用其特有流程。因而审核工作需要有多个不同的处理逻辑,因而代码中需要有不同的处理方式。
第一反应,方法中使用逻辑判断,如下:
public class CheckMoney {
public void check(String p_index) {
if ("340000".equals(p_index)) {
System.out.println("安徽的审核");
}else if ("320000".equals(p_index)) {
System.out.println("江苏的审核");
}else {
System.out.println("全国通用的审核");
}
}
}
暂不论这样是否会导致一个方法体无限扩大,但是这样的代码会导致在该方法调用前序方法体中无论哪个地方的审核,必须去处理传入参数(以上例子为了简单,只写一个参数,实际情况中可能参数不止一个),为了避免非特殊流程传入参数问题,我们可以如下修改,采用方法重载的方式进行处理:
public class CheckMoney {
public void check() {
System.out.println("全国通用的审核");
}
public void check(String p_index) {
if ("340000".equals(p_index)) {
System.out.println("安徽的审核");
} else if ("320000".equals(p_index)) {
System.out.println("江苏的审核");
}
}
}
好的,这样貌似已经解决传参数的问题,非特殊化的处理流程,只需要调用不带参数的方法即可,只有需要特殊处理的流程需要传入特殊参数。还分解出了特殊审核工作和一般性工作,似乎已经很好的解决了。
但是,新的问题又来了,随着业务要求越来越多,每个审核流程中代码不断增加,当前类文件已经达到3000行以上(夸张一下,用以说明问题)。严重影响了代码的可读性,给维护工作带来极大困难,这个时候,我们又想到一个好的解决方案,那就是引入接口,使用父接口+不同的实现类进行处理,如下:
改成接口checkMoney.java
public interface CheckMoney {
void check();
}
多个实现类:
public class CommCheckMoneyImpl implementsCheckMoney {
/**
* 全国通用审核方法
*/
@Override
public void check() {
// TODO Auto-generated method stub
System.out.println("全国通用的审核");
}
}
public class AhCheckMoneyImpl implementsCheckMoney {
/**
* 安徽审核方法
*/
@Override
public void check() {
// TODO Auto-generated method stub
System.out.println("安徽的审核");
}
}
public class JsCheckMoneyImpl implementsCheckMoney {
/**
*江苏审核方法
*/
@Override
public void check() {
// TODO Auto-generated method stub
System.out.println("江苏的审核");
}
}
调用方式如下:
// 全国审核
CheckMoney checkMoney = newCommCheckMoneyImpl();
checkMoney.check();
// 安徽审核
CheckMoney AncheckMoney = newAhCheckMoneyImpl();
AncheckMoney.check();
如此好了,有了统一的接口,不同的实现类进行代码的解藕。但是如何使程序能够采用统一的入口呢?这个时候我们就可以引入工厂(factory)。
一、 普通工厂
我们再上面接口基础上建立一个工厂类
chenkMoneyFacoty.java
public class CheckMoneyFactory {
public CheckMoney produce(String p_index) {
if ("340000".equals(p_index)) {
return new AhCheckMoneyImpl();
} else if ("320000".equals(p_index)) {
return new JsCheckMoneyImpl();
} else {
return new CommCheckMoneyImpl();
}
}
}
我们来测试下:
public class test {
public static void main(String[] args) {
CheckMoneyFactory factory = new CheckMoneyFactory();
CheckMoney checkMoney = factory.produce("340000");
checkMoney.check();
}
}
ok,如此,普通工厂正常工作,需要哪个实现类,统一工厂就给你生产一个实现。但是如果工厂中传入的p_index或者其他参数有误,有可能导致工厂生产失败,无法分配给你你想要的产品(实现类)。
二、 多个工厂方法模式,是对普通工厂方法模式的改进,多个工厂方法模式是提供多个工厂方法,分别创建对象。对CheckMoneyFactory进行如下修改:
public class CheckMoneyFactory {
publicCheckMoney produceAn() {
return newAhCheckMoneyImpl();
}
publicCheckMoney produceJs() {
return newJsCheckMoneyImpl();
}
publicCheckMoney produceComm() {
return newCommCheckMoneyImpl();
}
}
测试类如下:
public class test {
public static void main(String[] args) {
CheckMoneyFactory factory = newCheckMoneyFactory();
CheckMoney checkMoney = factory.produceAh();
checkMoney.check();
}
}
上述调用使用总是需要先创建一个工厂类,然后再生产处实现。
三、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,调用时候不再需要创建实例,直接调用即可。
public class CheckMoneyFactory {
public static CheckMoney produceAh() {
return new AhCheckMoneyImpl();
}
public static CheckMoney produceJs() {
return new JsCheckMoneyImpl();
}
public static CheckMoney produceComm() {
return new CommCheckMoneyImpl();
}
}
测试:
public class test {
public static void main(String[] args) {
CheckMoney checkMoney =CheckMoneyFactory.produceAh();
checkMoney.check();
}
}
如此这样,如果有新的省需要特殊化处理,只需要增加一个checkMoney的实现类,同时修改一下CheckMoneyFactory即可。
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种工厂,如果传入的字符串有误,第一种工厂不能正确创建对,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
这样的工厂是否就已经足够了呢?其实不然,上述方式的确已经很好的处理了解藕,但是并没有将解藕进行到底,有新的要求时候依然需要去修改现有的程序,扩展性并没有想象中那么强。所以下面我们继续研究。。
四、 抽象工厂模式:
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
如下代码:
一个虚拟工厂
public interface CheckMoneyFactory {
public CheckMoney produce();
}
三个工厂实现类
public class CommCheckMoneyFactoryImpl implementsCheckMoneyFactory {
@Override
public CheckMoney produce() {
// TODO Auto-generated method stub
return new CommCheckMoneyImpl();
}
}
public class AhCheckMoneyFactoryImpl implementsCheckMoneyFactory {
@Override
public CheckMoney produce() {
// TODO Auto-generated method stub
return new AhCheckMoneyImpl();
}
}
public class JsCheckMoneyFactoryImpl implementsCheckMoneyFactory {
@Override
public CheckMoney produce() {
// TODO Auto-generated method stub
return new JsCheckMoneyImpl();
}
}
测试代码如下:
public class test {
public static void main(String[] args) {
CheckMoneyFactory ahCheckMoneyFactoryImpl = new AhCheckMoneyFactoryImpl();
CheckMoney ahCheckMoney = ahCheckMoneyFactoryImpl.produce();
ahCheckMoney.check();
}
}
好了,至此我们总结一下:
如果广东需要特殊化的审核流程,我们需要怎么做?是否需要修改现有审核逻辑中的程序?答案是完全不需要,我们增加一个GdCheckMoneyImpl 实现CheckMoney接口,再写一个GdCheckMoneyFactoryImpl实现CheckMoneyFactory接口,然后就可以放心的使用了。完全不会对现有的其它地方的审核流程进行修改,完全避开了现有逻辑处理,保证现有功能的继续服务,同时又能实现对新需求的完美扩展。