设计模式-工厂方法模式
一、模式定义
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
二、模式结构
我们可以看到工厂方法模式一共分为四个部分:
- 抽象工厂(AbstractCreator)角色:担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创建对象的工厂类必须继承或者实现这个接口,在实际的系统中,这个角色常常有Java抽象类来实现。
- 具体工厂(ConcreteCreator)角色:担任这个角色的是实现了抽象工厂接口的具体Java类。具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
- 抽象产品(AbstractProduct)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在实际应用中这个角色常常由Java的抽象类来实现。
- 具体产品(ConcreteProduct)角色:这个角色实现了抽象产品角色所声明的接口,工厂方法所创建的每一个对象都是某个具体产品角色的实例。
三、模式动机
1、解决简单工厂模式中存在的“上帝类”的问题,将具体的生产任务放到子类中去。
2、解决简单工厂模式工厂角度不符合“开-闭”原则的问题。
3、解决简单工厂模式不能使用继承的问题。
四、实例分析
我们继续来分析上一篇博客中土豪开车的问题,我们使用了简单工厂模式后,程序好像已经很合理了,并且从实际生活来看也是比较符合逻辑的。但是可能会存在以下的问题。
1、如果土豪家里又买了很多车,那么Driver(工厂类)将会非常大,每次买一辆新的车都需要在这个工厂类中添加一个if-else语句,如果有的车坏了不能开了,还需要将对应的if-else语句删除,所以很麻烦,也不符合“开-闭”原则。
2、所有的产品创建都集中在Driver类中,一旦写错一个标点,所有的产品便都不能生产了。(可以想象成,只有一位司机师傅,这位司机师傅会开所有的车,一旦这一位司机师傅生病了,那么所有的汽车就都不能开了)。
所以我们可以使用工厂方法模式以下的改进。
首先,定义一个抽象的工厂类(Driver),这个抽象工厂类中有定义好的抽象方法getCar();用来告诉具体的工厂如何来获得汽车。
package com.myfactory.factory;
public abstract class Driver {
public abstract Car getCar();
}
然后,每一种车都配一个专门的司机,这个司机只负责或者一种车。
package com.myfactory.factory;
public class BenzDriver extends Driver{
public Car getCar() {
return new Benz();
}
}
这里只写了Benz车的类,其他的车的类和这个类基本相同,不再贴出代码,最后会附上源码。
在客户端中,创建对应的Driver类,让特定的Driver来生产特定的汽车。
package com.myfactory.factory;
public class Main {
public static void main(String[] args) throws Exception {
//奔驰车司机
Driver driver = new BenzDriver();
//今天想做奥迪车
Car car = driver.getCar();
//开车
car.drive();
}
}
通过改进,我们看到不再存在“上帝类”,有具体的工厂类来负责生产特定的产品,以后想要添加新的车的时候不必修改现有的工厂和产品的代码,只需要添加新的产品类和具体工厂类就可以了,不管是产品角度还是工厂角度都更加符合“开-闭”原则。
五、模式优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
六、模式缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
七、适用场景
在以下情况下可以使用工厂方法模式:
1、一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
2、一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
八、模式在JDK中的应用
1、Object中的toString方法:Object仅仅定义了返回String,由Object的子类来决定如何生成这个字符串。
2、抽象类java.util.Calendar的getInstance方法将根据不同的情况返回不同的Calendar子类的对象。
源码下载:http://download.csdn.net/detail/xingjiarong/9294193
感谢您的阅读,您的支持是我写博客动力。