设计模式(六)——JDK中的那些工厂方法

l2wonxgc 8年前
   <p>在 <a href="http://www.open-open.com/lib/view/open1460474119776.html" rel="nofollow,noindex">设计模式(五)——工厂方法模式</a> 中介绍了工厂方法模式。本文通过介绍JDK源码中用到的工厂方法,在上篇文章的基础上深入理解一下工厂方法。</p>    <h2>再谈工厂方法</h2>    <p>在 <a href="http://www.open-open.com/lib/view/open1460474119776.html" rel="nofollow,noindex">设计模式(五)——工厂方法模式</a> 中用整篇介绍了工厂方法模式。为什么要再谈呢?因为很多人走进了一个误区。认为工厂方法模式就是要严格包含抽象产品、具体产品、抽象工厂和具体工厂等角色。其实并不是这样的。</p>    <p>有时候也会创建不使用多态性创建对象的工厂方法,以得到使用工厂方法的其他好处。</p>    <p>如果抛开设计模式的范畴,“工厂方法”这个词也可以指 <strong>作为“工厂”的方法</strong> ,这个方法的主要目的就是创建对象,而这个方法不一定在单独的工厂类中。这些方法通常作为静态方法,定义在方法所实例化的类中。</p>    <p>每个工厂方法都有特定的名称。在许多面向对象的编程语言中,构造方法必须和它所在的类具有相同的名称,这样的话,如果有多种创建对象的方式(重载)就可能导致歧义。工厂方法没有这种限制,所以可以具有描述性的名称。举例来说,根据两个实数创建一个复数,而这两个实数表示直角坐标或极坐标,如果使用工厂方法,方法的含义就非常清晰了。当工厂方法起到这种消除歧义的作用时,构造方法常常被设置为私有方法,从而强制客户端代码使用工厂方法创建对象。</p>    <pre>  <code class="language-java">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);  </code></pre>    <p>上面的代码也可以叫做使用了工厂方法模式。同样是定义了一个工厂方法,方法的作用就是生成一个新的对象。</p>    <h2>JDK中的工厂方法模式</h2>    <p>看过前面的介绍和上一篇文章的介绍,相信读者对工厂方法有了一定的认识,那么能不能试着回答这样一个问题:什么情况下应该使用工厂方法模式?</p>    <p>其实这个问题很简单。很多时候我们在自己的代码中会不自觉的会使用到工厂方法模式。考虑以下情况:</p>    <p>创建对象需要大量重复的代码。</p>    <p>创建对象需要访问某些信息,而这些信息不应该包含在复合类中。</p>    <p>创建对象的生命周期必须集中管理,以保证在整个程序中具有一致的行为。</p>    <p>遇到上面的情况时,你是不是会”很自觉”的把创建对象的代码封装到一个方法中呢?这个方法就是工厂方法。其实这就是工厂方法的思想。</p>    <p>在JDK中,也有很多使用工厂方法模式的代码。下面就介绍几个典型的用法。</p>    <h3>Collection中的iterator方法</h3>    <p>java.util.Collection 接口中定义了一个抽象的 iterator() 方法,该方法就是一个工厂方法。</p>    <p>对于 iterator() 方法来说 Collection 就是一个根抽象工厂,下面还有 List 等接口作为抽象工厂,再往下有 ArrayList 等具体工厂。</p>    <p>java.util.Iterator 接口是根抽象产品,下面有 ListIterator 等抽象产品,还有 ArrayListIterator 等作为具体产品。</p>    <p>使用不同的具体工厂类中的 iterator 方法能得到不同的具体产品的实例。</p>    <p><img src="https://simg.open-open.com/show/fdad6b8ec2ed108e18bcf6aed33aa8cb.png"></p>    <h3>JDBC数据库开发</h3>    <p>在使用 JDBC 进行数据库开发时,如果数据库由MySQL改为Oracle或其他,则只需要改一下数据库驱动名称就可以,其他都不用修改(前提是使用的都是标准SQL语句)。或者在Hibernate框架中,更换数据库方言也是类似道理。</p>    <h3>连接邮件服务器框架</h3>    <p>如果需要设计一个连接邮件服务器的框架,那么就要考虑到连接邮件服务器有几种方式: POP3 、 SMTP 、 HTTP 。就可以定义一个连接邮件服务器接口,在此接口中定义一些对邮件操作的接口方法,把这三种连接方式封装成产品类,实现接口中定义的抽象方法。再定义抽象工厂和具体工厂,当选择不同的工厂时,对应到产生相应的连接邮件产品对象。采用这种工厂方法模式的设计,就可以做到良好的扩展性。比如某些邮件服务器提供了WebService接口,只需要增加一个产品类和工厂类就可以了,而不需要修改原来代码。</p>    <h3>其他工厂方法</h3>    <p>除上面介绍的典型的使用工厂方法模式的用法外,在JDK中还有几处使用了工厂方法来创建对象。其中包括:</p>    <pre>  <code class="language-java">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()  </code></pre>    <p>读者感兴趣可以找出来看一看,这些方法都是用于返回具体对象。上面大部分方法都和反射有关,因为反射创建对象的过程比较复杂,封装成工厂方法更易于使用。</p>    <h2>参考资料</h2>    <p><a href="/misc/goto?guid=4959670588581190565" rel="nofollow,noindex">工厂方法模式</a></p>    <p>(全文完)</p>    <p>来自: <a href="/misc/goto?guid=4959670588673076243" rel="nofollow">http://www.hollischuang.com/archives/1408</a></p>