转载自:http://www.impeng.org/factory-method.html
工厂方法模式属于创建型模式,即负责对象的创建。
为什么要有工厂?工厂的作用是将对象的创建和调用分离开来,对象的创建由工厂负责。因为在很多时候,对象的创建并不是new一个这么简单,其中可能会包含很多初始化函数。如果用传统的new方法实例化对象,当需要多次实例化时,会造成大量代码重复;而有工厂存在时,调用者只需要向工厂发出请求,工厂即返回所需对象,并且对调用者屏蔽了对象创建的细节,从而可以让调用者只需关注对象的使用,而不是其创建上。
要了解工厂方法模式(FactoryMethod),我们首先需要了解简单工厂(SimpleFactory,不是一种模式):
简单工厂可以概括为一句话:通过向工厂的产品创建方法中传入不同的参数,工厂返回相应的产品实例。
仔细观察上面的代码及注释,可以看出简单工厂的特点是:
- 包含有抽象产品(Car类)、具体产品(BMW类, Benz类)和工厂(CarFactory类);
- 工厂的构造函数对外不可见,通过一个静态的产品创建方法来创建产品;
- 调用者通过向产品创建方法中传入不同的参数,来得到对应的产品实例。
但是,思考如下问题:当我们需要新增一个产品的时候(比如,需要增加Buick车),将需要在工厂的产品创建方法中新增一个判断分支,也就是说需要修改工厂类的源代码。这显然不符合软件工程中最重要的开放关闭原则(OCP,Open-Close Principle;对扩展开放,对修改关闭)。
如何解决这个矛盾?在SimpleFactory中,一个工厂负责了所有产品的创建,当需要新增产品时,需要修改工厂类,这显然难以实现可扩展。由于可扩展大都是由接口来实现的,那么我们就可以抽象出工厂的特性(比如都有产品的创建方法create()),形成接口CarFactory,然后通过具体的工厂(实现了接口)来生产对应的产品,一个工厂只负责一种产品的创建。
这样,当需要新增产品时,我们可以通过三个步骤实现:
- 编写好新增的产品类(Buick车,实现抽象接口Car);
- 创建一个BuickFactory,实现抽象接口CarFactory(即实现其create方法);
- 调用者创建BuickFactory的实例,生产对应的Buick车。
具体代码如下:
由上可知,工厂方法模式具有以下的特点:
- 包含有抽象产品(Car类)、具体产品(如Buick类)、抽象工厂(CarFactory类)、具体工厂(如BuickFactory类);
- 工厂需要实例化,即存在默认构造函数;
- 一个具体工厂只负责一个具体产品的生产。
由此可知,工厂方法模式很好地遵循了开闭原则,实现了程序的可扩展。如果有新的产品需要生产,只需要新建一个具体工厂(实现抽象工厂接口)来负责该产品的创建;由于所有代码都面向接口实现,调用者只需要知道具体工厂的名称即可(如代码中的new BuickFactory();),将具体产品(具体工厂)返回给抽象产品(抽象工厂),不需要修改被调用模块的源代码,调用模块也只需要修改具体工厂的类名,提高了可维护性。
另外,如果将具体工厂名称的信息通过Java反射机制或IOC方式(如Spring)注入到客户端中,这样连调用处的代码也不用修改了,只需要在配置文件中修改一下配置信息就行了。这样,整个程序就实现了源代码完全无修改的可扩展。