这儿有两个关键点:
1>工厂方法满足开闭原则么?
2>工厂方法到底用在什么场合?
好像模式的书上都写着工厂方法满足开闭原则...
但是我认为它并不满足开闭, 不知道是不是我的认识有错误
故再此发文一篇,权当讨论与提高.
哪么先从简单工厂说起,好像所有的讲设计模式的书籍里面工厂模式都是从简单工厂开始的.
简单工厂是公认的部分支持开闭原则的.
众所周知工厂模式就是为了解决New这个玩意儿的,先来一个没有用到工厂的例子
import java.io.*;
interface ITest{
String testFun();
}
class TestA implements ITest{
public String testFun(){
return "A;testFun";
}
}
class TestB implements ITest{
public String testFun(){
return "B;testFun";
}
}
public class TestFactory{
public static void main(String args[]) throws IOException{
//声明一个变量引用
ITest test = null;
//根据用户输入决定实例化具体的对象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
if(line.equals("A")){
test = new TestA();
}
else if(line.equals("B")){
test = new TestB();
}
//使用对象
if (test != null){
System.out.println(test.testFun());
}
}
}
这儿使用了 new 具体的类,不满足开闭原则
因为修改或者增加,new这个地方都要改动
比如说我们现在增加了一个类C,那么客户端这儿就不可避免要改动.
如果这样的逻辑在多个客户端用到,就必须都做改动,显然是不合理的.
基于 "对可变性的封装" 的指导思想.
把new这个地方的工作封装到一个类里面,于是就有了简单工厂.
class SampleFactory{
public static ITest create(String ar){
if(ar.equals("A")){
return new TestA();
}
else if(ar.equals("B")){
return new TestB();
}
return null;
}
}
然后客户端调用改为:
public static void main(String args[]) throws IOException{
//声明一个变量引用
ITest test = null;
//根据用户输入决定实例化具体的对象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
//调用简单工厂创建对象
test = SampleFactory.create(line);
//使用对象
if (test != null){
System.out.println(test.testFun());
}
}
于是这儿就能适应变化了,如果新加入了C类型,就不用去修改所有使用的客户端了.客户端满足开闭了,
但是工厂又必须修改,而且吧其他类型的创建逻辑影响到了.那咋办捏?
又基于"对可变性的封装" 的指导思想,对每一个类型的New 都弄一个工厂,这样有扩展就不得影响
道其他工厂了, 这个就是工厂方法模式, 好像满足了 开闭原则了,
interface IFactoryMathod{
ITest create();
}
class FactoryA implements IFactoryMathod{
public ITest create(){
return new TestA();
}
}
class FactoryB implements IFactoryMathod{
public ITest create(){
return new TestB();
}
}
然后 客户端调用变成:
public static void main(String args[]) throws IOException{
//声明一个变量引用
ITest test = null;
//声明一个工厂
IFactoryMathod factory = null;
//根据用户输入决定实例化具体的对象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
//调用工厂方法创建对象
if(line.equals("A")){
factory = new FactoryA();
}
else if(line.equals("B")){
factory = new FactoryB();
}
else{
factory = new FactoryB();
}
test = factory.create();
//使用对象
if (test != null){
System.out.println(test.testFun());
}
}
但是现在问题有来了,客户端的这段代码又瓜了.对客户端来说,又不满足开闭了.
我们转了一圈结果发现又回到开始的地方了...晕??
哪么我认为:工厂方法仍然不满足开闭, 因为只要在代码里面用了New ,就不可能满足开闭,如果有添加
总要修改到现有代码的某一个部分.
这儿就需要引入反射,消除了New,从配置文件里面读取需要创建的对象.比如说Spring就是这么干的.
然后说了半天,从上面的这个例子好像发现,那个工厂方法没什么用处得,完全就是用一个方法把new
包起来,大大的有脱裤子放屁的嫌疑..
到底怎么回事情啦,不可能大名顶顶的工厂方法就这样子??
哪么就进入下面的一个论点: 工厂方法的使用场合
好像有了反射.工厂方法就可以下岗了...
的确,我认为仅仅在工厂方法里面写一个New XXXX 的话,哪么这样用工厂方法的确快下岗了..
但是我觉得真正体现工厂方法的意义的在这儿:
还是 拿出 我们的指导思想(马克思列宁思想...哈哈哈) "对可变性的封装"
当对象的创建不仅仅是一个new XXXX,包含复杂的业务逻辑,而且可以面临巨大的变动.
比如说: 有个需求是对象的创建需要是从文件序列化出来,如果没有才New一个,属性设置为默认,
万一那天有要求分布式应用,不能从文件里面反序列,需要改为从数据库里面找到属性并创建一个出来.
总不可能在每个调用这个类的客户端端口都去写上这么一大堆 if else的业务逻辑三.
如果以后再有改动,不就麻烦了..
这个时候就需要工厂方法,把可变的封装到工厂方法里面,
如果以后有变动或者增加,我们就只是需要修改或者扩展具体的工厂方法类,其他的都不会受到干扰.
再结合反射,将工厂方法类放到配置文件,这样就能真正的 满足开闭原则.
可能有同学有提出来问题:我何不在具体类的构造函数里面去做哪么一大堆啦.这样有改动的化,
我修改一下那个构造函数不久得了.反正用了反射,仍然满足开闭原则.
但是如果那个地方的代码变动的可能很大,你修改了构造函数,哪么这个类的其他地方就受到干扰了
站在这个问题的角度说,就是: 当一个类的构造逻辑频繁变动,哪么就需要把他封装,于是把这儿的逻辑放到工厂方法里面了.
这儿有体现了 "对可变性的封装" 的思想
工厂方法模式并不是因为简单工厂方法不满足开闭原则而引入
而是因为:类的构造逻辑复杂且多变,为了将构造逻辑封装而引入
哪么现在得出结论:
1>工厂方法并不真正满足开闭原则,但是结合反射和配置文件能够满足.
2>工厂方法使用场合 : 类的构造逻辑复杂且多变,为了将构造逻辑封装而引入
如果仅仅是在工厂方法里面写一个New,而且也不会发生变化.就没有使用工厂方法的意义了.
然后还有这个 思想我觉得比较重要
"对可变性的封装",或者说 "对不确定性的封装"
OOAD的核心思想啊!!!
欢迎讨论...写这些的目的就是为了共同进步,有什么错误或者不足,欢迎指出....