工厂方法模式
工厂方法模式,是用来创建一个对象的。
它的意图是定义一个用于创建对象的接口,让子类决定实例化哪一个类。将实例化延迟到其子类。
当一个类需要实例化另一个类的派生类,但不知道具体是哪个派生类。工厂方法模式允许让派生类来做决定。
举例来说,一个生产汽车的厂家,需要生产一辆汽车并进行相关工作,例如测试之类的,那么由高层来指定生产哪种型号的汽车,如果只使用一个汽车工厂的话,若今天生产A型汽车,所用的是A型汽车的生产工具和配置,到了明日要生产B型汽车,那么只能把这个汽车工厂变成适合生产B型汽车,改变原有的生产工具和配置。
这就是变化。
这种变化需要消耗大量的人力无力,特别是变化无比频繁的时候:今天生产A型汽车、明天生产B型汽车、后天生产C型、大后天又需要生产A型……
每天都需要不断地改变这个汽车工厂,但这并不是最糟糕的。将它看成程序语言的话,我们就可以看出变化带来的可怕之处。
public class App
{
public static void main()
{
//主程序是做决策的高层,今天他要生产A型汽车
carFactory factory = new carFactory();
CarA carA = factory.create();
}
}
//这是汽车工厂
public class carFactory
{
public CarA create(){return new CarA;}
public void someMethodForCreateCarA_tool1(){}
public void someMethodForCreateCarA()_tool2(){}
……
}
现在到了第二天,高层决定要生产B型汽车,那么哪些会改变了呢?
首先是主程序,这代表高层的决策,是非变不可的。
main函数中变成这样:
carFactory factory = new carFactory();
CarB carB = factory.create();
然后要变的是汽车工厂
public class carFactory
{
public CarB create(){return new CarB;}
public void someMethodForCreateCarB_tool1(){}
public void someMethodForCreateCarB_tool2(){}
……
}
看,整个汽车工厂都要改变了,变成适合生产B型汽车。
下面我将展示变化带来的更可怕的灾难。
一辆车生产出来,它还要做一些其他事才行吧,比如测试。
那么,
class testCar
{
public test(CarA carA){}
public sometestforcarA(){}
……
}
这样的话,连整个测试框架都要更改了,从程序角度看,如果carA和carB的接口一致的话,或许我们只需要将CarA的名字替换成CarB便行了,但这也是改变,改变会带来重新编译,重新部署,还有无法预料的问题。
继测试之后,如果还有一系列工作,那么这一系列的工作将全部改变!因为这些工作都与汽车这个对象有着强依赖关系!
这就是需要工厂方法模式的地方。
想象一下,我们为没一种型号的汽车配备一个汽车工厂(这在现实中似乎不太可能),若要生产A型汽车便送到汽车工厂A去生产,生产B型汽车则送到B型汽车工厂,如果没B型汽车工厂则造一个……
从现实的角度看似乎不太合理,那让我们从程序的角度出发。
构造了一个汽车工厂的抽象类,当我们需要生产A型汽车时,便建造汽车工厂A
需要生产B型汽车呢?
这样的话,我们便不需要改变工厂了,我们做的已成为了扩展。
故设计模式就是这样:拒绝改变,允许扩展!
接下来再看,构造不同的汽车工厂,虽然减少了改变,但测试汽车这些工作进行的时候会怎么样呢?强依赖关系依然存在!
所以我们要给减少这种强依赖。(依赖是不能杜绝的,只能将它减少到一个可控制的范围内!)
给汽车也构建一个抽象类,需要A型汽车时:
需要B型汽车时:
然后我们还要给它做一系列测试等工作
这样的话,我们已经将所有的改变集中到主函数中了
下一次,当变化来临时,比如高层发话要生产X型汽车,我们只需改变两点,一是构建新的汽车工厂、新的汽车类(这两个类可以在一个文件中),二是主函数,将工厂实例改为CarXFactory。
除此之外其他都无需改变。
甚至,我们可以将构建工厂实例的过程动态实现,根据配置文件,让程序自动创建对象实例。新的汽车工厂和汽车类编译成DLL递交给客户即可,完全不再需要动源程序了!
-----------------------------------------------
以上是我看了李建忠老师的视频后的一点感悟和愚见。