原文链接:
http://tianweili.github.com/blog/2015/03/09/factory-method-pattern/
介绍
工厂方法模式定义:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses。定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式是简单工厂模式的衍生,核心工厂类不再负责具体产品的创建,而是一个抽象工厂的角色,仅仅定义子类工厂必须实现的接口。这样进一步抽象的好处是可以使系统在不修改具体工厂类的方式下引进新的产品。
工厂方法模式的对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不再负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。
工厂方法模式的核心和关键点是对工厂也进行了抽象。
UML类图
工厂方法模式的UML类图如下所示:
{% img /design-pattern/factory-method-uml.png %}
在上面的UML图中主要包含了四中角色:
抽象工厂(Abstract Factory)角色
抽象工厂角色是工厂方法模式的核心,主要定义了具体工厂子类必须要实现的接口,并且所有创建对象的工厂类都必须实现该方法,与应用程序无关。
具体工厂(Concrete Factory)角色
这是实现抽象工厂的具体工厂类,包含与应用程序密切相关的逻辑,要受到应用程序的调用以创建产品对象。
抽象产品(Abstract Product)角色
具体产品对象的共同父类,由它来定义所有产品的共同接口。
具体产品(Concrete Product)角色
具体产品是简单工厂模式的创建目标,所有创建的对象都是某个具体产品类的实例。
代码示例
public interface Product {
public void method();
}
public class ProductA implements Product{
@Override
public void method() {
System.out.println("ProductA Method ...");
}
}
public class ProductB implements Product{
@Override
public void method() {
System.out.println("ProductB Method ...");
}
}
public interface Factory {
public Product create();
}
public class FactoryA implements Factory{
@Override
public Product create() {
return new ProductA();
}
}
public class FactoryB implements Factory{
@Override
public Product create() {
return new ProductB();
}
}
public class Client {
public static void main(String[] args) {
Factory factory = new FactoryA();
Product product = factory.create();
product.method();
factory = new FactoryB();
product = factory.create();
product.method();
}
}
运行结果:
ProductA Method ...
ProductB Method ...
与简单工厂模式的区别
工厂方法模式是简单功能工厂模式的衍生,它将工厂也抽象出来,定义了一个抽象工厂,将产品对象的实例化过程推到了下面的各个子类工厂,而且子类工厂与产品也是一一对应的。这是两者最大的区别。
工厂方法模式多了一个抽象工厂。
工厂方法模式支持多态。
优点
解决了许多简单工厂模式中出现的问题。
完全符合”开闭原则”,具有可扩展性。
更为复杂的层次结构,可以应付产品结果更为复杂的场合。
工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其它实现类都不需要关心,符合迪米特法则。只依赖产品的抽象,符合依赖倒置原则。使用产品子类替换产品父类,符合里氏替换原则。
缺点
抽象出了一个抽象工厂,添加了具体工厂子类,会提高系统的复杂度。所以对于一些复杂的创建过程使用工厂方法模式才比较合适。
适用场景
工厂方法模式是使用非常频繁的设计模式之一,在系统设计中几乎随处可见。简单工厂模式、工厂方法模式、抽象工厂模式这三个设计模式都有一些类似的特性,所以在适用场景上也都是类似的。
首先,作为一个创建型模式,在任何需要生成复杂对象的地方都可以使用工厂方法模式。但是要注意对于一些生成对象简单,特别是只需要通过new来生成对象的地方,就不需要使用工厂方法模式了。因为如果使用工厂方法模式,就需要引入一个工厂类,会增加系统的复杂度。
另外,在需要一个可扩展性强的系统设计中,可以考虑使用工厂方法模式。比如产品对象预期可能会经常造成增加或删减等,使用工厂方法模式来设计可以使系统更加灵活。
应用实例
Collection中的iterator方法
java.util.Collection
接口中的iterator()
方法就是一个工厂方法。对于iterator方法来说Collection就是一个根抽象工厂,下面还有List等接口作为抽象工厂,再往下有ArrayList等具体工厂。
java.util.Iterator
接口是根抽象产品,下面有ListIterator等抽象产品,还有ArrayListIterator等作为具体产品。
获得产品代码:
Collection c = new ArrayList();
Iterator i = c.iterator();
基本的UML类图如下所示:
{% img /design-pattern/collection-iterator-uml.png %}
JDBC数据库开发
在使用JDBC进行数据库开发时,如果数据库由MySQL改为Oracle或其他,则只需要改一下数据库驱动名称就可以,其他都不用修改(前提是使用的都是标准SQL语句)。或者在Hibernate框架中,更换数据库方言也是类似道理。
连接邮件服务器框架
如果需要设计一个连接邮件服务器的框架,那么就要考虑到连接邮件服务器有几种方式:POP3、SMTP、HTTP。就可以定义一个连接邮件服务器接口,在此接口中定义一些对邮件操作的接口方法,把这三种连接方式封装成产品类,实现接口中定义的抽象方法。再定义抽象工厂和具体工厂,当选择不同的工厂时,对应到产生相应的连接邮件产品对象。采用这种工厂方法模式的设计,就可以做到良好的扩展性。比如某些邮件服务器提供了WebService接口,只需要增加一个产品类和工厂类就可以了,而不需要修改原来代码。
网页爬虫解析
在一个项目中,需要实现的需求是这样的:公司有很多站点,需要带着指定的一些关键字去网站上爬取广告、文章、产品等信息解析下来,而不同的站点风格各异,爬取和解析的方式也各不相同。
所以这里就用到了工厂方法模式,抽象出一个抽象产品,定义公共接口,爬取解析某个站点就封装成一个产品对象。再抽象出抽象工厂,使一些具体工厂实现抽象工厂定义的调用产品接口,每个具体工厂对应一个产品。
作者:李天炜
原文链接:http://tianweili.github.com/blog/2015/03/09/factory-method-pattern/
转载请注明作者和文章出处,谢谢。