新专题:设计模式,我会在博客(http://www.hollischuang.com)及微信公众号(hollischuang)同步更新,欢迎共同学习。
在设计模式(五)——工厂方法模式中介绍了工厂方法模式。本文通过介绍JDK源码中用到的工厂方法,在上篇文章的基础上深入理解一下工厂方法。
再谈工厂方法
在设计模式(五)——工厂方法模式中用整篇介绍了工厂方法模式。为什么要再谈呢?因为很多人走进了一个误区。认为工厂方法模式就是要严格包含抽象产品、具体产品、抽象工厂和具体工厂等角色。其实并不是这样的。
有时候也会创建不使用多态性创建对象的工厂方法,以得到使用工厂方法的其他好处。
如果抛开设计模式的范畴,“工厂方法”这个词也可以指作为“工厂”的方法,这个方法的主要目的就是创建对象,而这个方法不一定在单独的工厂类中。这些方法通常作为静态方法,定义在方法所实例化的类中。
每个工厂方法都有特定的名称。在许多面向对象的编程语言中,构造方法必须和它所在的类具有相同的名称,这样的话,如果有多种创建对象的方式(重载)就可能导致歧义。工厂方法没有这种限制,所以可以具有描述性的名称。举例来说,根据两个实数创建一个复数,而这两个实数表示直角坐标或极坐标,如果使用工厂方法,方法的含义就非常清晰了。当工厂方法起到这种消除歧义的作用时,构造方法常常被设置为私有方法,从而强制客户端代码使用工厂方法创建对象。
class Complex {
public static Complex fromCartesianFactory(double real, double imaginary) {
return new Complex(real, imaginary);
}
public static Complex fromPolarFactory(double modulus, double angle) {
return new Complex(modulus * cos(angle), modulus * sin(angle));
}
private Complex(double a, double b) {
//...
}
}
Complex product = Complex.fromPolarFactory(1, pi);
上面的代码也可以叫做使用了工厂方法模式。同样是定义了一个工厂方法,方法的作用就是生成一个新的对象。
JDK中的工厂方法模式
看过前面的介绍和上一篇文章的介绍,相信读者对工厂方法有了一定的认识,那么能不能试着回答这样一个问题:什么情况下应该使用工厂方法模式?
其实这个问题很简单。很多时候我们在自己的代码中会不自觉的会使用到工厂方法模式。考虑以下情况:
创建对象需要大量重复的代码。
创建对象需要访问某些信息,而这些信息不应该包含在复合类中。
创建对象的生命周期必须集中管理,以保证在整个程序中具有一致的行为。
遇到上面的情况时,你是不是会"很自觉"的把创建对象的代码封装到一个方法中呢?这个方法就是工厂方法。其实这就是工厂方法的思想。
在JDK中,也有很多使用工厂方法模式的代码。下面就介绍几个典型的用法。
Collection中的iterator方法
java.util.Collection
接口中定义了一个抽象的iterator()
方法,该方法就是一个工厂方法。
对于iterator()
方法来说Collection
就是一个根抽象工厂,下面还有List
等接口作为抽象工厂,再往下有ArrayList
等具体工厂。
java.util.Iterator
接口是根抽象产品,下面有ListIterator
等抽象产品,还有ArrayListIterator
等作为具体产品。
使用不同的具体工厂类中的iterator
方法能得到不同的具体产品的实例。
JDBC数据库开发
在使用JDBC
进行数据库开发时,如果数据库由MySQL改为Oracle或其他,则只需要改一下数据库驱动名称就可以,其他都不用修改(前提是使用的都是标准SQL语句)。或者在Hibernate框架中,更换数据库方言也是类似道理。
连接邮件服务器框架
如果需要设计一个连接邮件服务器的框架,那么就要考虑到连接邮件服务器有几种方式:POP3
、SMTP
、HTTP
。就可以定义一个连接邮件服务器接口,在此接口中定义一些对邮件操作的接口方法,把这三种连接方式封装成产品类,实现接口中定义的抽象方法。再定义抽象工厂和具体工厂,当选择不同的工厂时,对应到产生相应的连接邮件产品对象。采用这种工厂方法模式的设计,就可以做到良好的扩展性。比如某些邮件服务器提供了WebService接口,只需要增加一个产品类和工厂类就可以了,而不需要修改原来代码。
其他工厂方法
除上面介绍的典型的使用工厂方法模式的用法外,在JDK中还有几处使用了工厂方法来创建对象。其中包括:
java.lang.Proxy#newProxyInstance()
java.lang.Object#toString()
java.lang.Class#newInstance()
java.lang.reflect.Array#newInstance()
java.lang.reflect.Constructor#newInstance()
java.lang.Boolean#valueOf(String)
java.lang.Class#forName()
读者感兴趣可以找出来看一看,这些方法都是用于返回具体对象。上面大部分方法都和反射有关,因为反射创建对象的过程比较复杂,封装成工厂方法更易于使用。
我又来了,您高兴吗?!
java.lang.Proxy#newProxyInstance()这个不是简单工厂模式吗?