﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-我＆我的爱，我的幸福生活</title><link>http://www.blogjava.net/fish001118/</link><description>展现自我，放飞理想！</description><language>zh-cn</language><lastBuildDate>Sat, 18 Apr 2026 15:01:07 GMT</lastBuildDate><pubDate>Sat, 18 Apr 2026 15:01:07 GMT</pubDate><ttl>60</ttl><item><title>设计模式之Factory</title><link>http://www.blogjava.net/fish001118/archive/2006/02/19/31561.html</link><dc:creator>飞翔天空</dc:creator><author>飞翔天空</author><pubDate>Sun, 19 Feb 2006 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/fish001118/archive/2006/02/19/31561.html</guid><wfw:comment>http://www.blogjava.net/fish001118/comments/31561.html</wfw:comment><comments>http://www.blogjava.net/fish001118/archive/2006/02/19/31561.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fish001118/comments/commentRss/31561.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fish001118/services/trackbacks/31561.html</trackback:ping><description><![CDATA[<P align=center><A href="http://www.jdon.com/aboutme.htm">板桥里人</A> （转载请保留）</P>
<P><B><I>工厂模式定义<SPAN lang=EN-US>:提供创建对象的接口.</SPAN></I></B></P>
<P><B><I>为何使用<SPAN lang=EN-US>?</SPAN></I></B><SPAN lang=EN-US><BR>工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式，工厂模式在Java程序系统可以说是随处可见。</SPAN></P>
<P>为什么工厂模式是如此常用？因为工厂模式就相当于创建实例对象的<SPAN lang=EN-US>new，我们经常要根据类Class生成实例对象，如A a=new A() 工厂模式也是用来创建实例对象的，所以以后new时就要多个心眼，是否可以考虑实用工厂模式，虽然这样做，可能多做一些工作，但会给你系统带来更大的可扩展性和尽量少的修改量。</SPAN></P>
<P>我们以类<SPAN lang=EN-US>Sample为例， 如果我们要创建Sample的实例对象:</SPAN></P>
<P><SPAN lang=EN-US>Sample sample=new Sample();</SPAN></P>
<P>可是，实际情况是，通常我们都要在创建<SPAN lang=EN-US>sample实例时做点初始化的工作,比如赋值 查询数据库等。</SPAN></P>
<P>首先，我们想到的是，可以使用<SPAN lang=EN-US>Sample的构造函数，这样生成实例就写成:</SPAN></P>
<P><SPAN lang=EN-US>Sample sample=new Sample(参数);</SPAN></P>
<P>但是，如果创建<SPAN lang=EN-US>sample实例时所做的初始化工作不是象赋值这样简单的事，可能是很长一段代码，如果也写入构造函数中，那你的代码很难看了（就需要Refactor重整）。</SPAN></P>
<P>为什么说代码很难看，初学者可能没有这种感觉，我们分析如下，初始化工作如果是很长一段代码，说明要做的工作很多，将很多工作装入一个方法中，相当于将很多鸡蛋放在一个篮子里，是很危险的，这也是有背于<SPAN lang=EN-US>Java面向对象的原则，面向对象的封装(Encapsulation)和分派(Delegation)告诉我们，尽量将长的代码分派“切割”成每段，将每段再“封装”起来(减少段和段之间偶合联系性)，这样，就会将风险分散，以后如果需要修改，只要更改每段，不会再发生牵一动百的事情。</SPAN></P>
<P>在本例中，首先，我们需要将创建实例的工作与使用实例的工作分开<SPAN lang=EN-US>, 也就是说，让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。</SPAN></P>
<P>这时我们就需要<SPAN lang=EN-US>Factory工厂模式来生成对象了，不能再用上面简单new Sample(参数)。</SPAN>还有<SPAN lang=EN-US>,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.</SPAN>现在<SPAN lang=EN-US>Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:</SPAN></P>
<P><SPAN lang=EN-US>Sample mysample=new MySample();<BR>Sample hissample=new HisSample();</SPAN></P>
<P>随着项目的深入<SPAN lang=EN-US>,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.</SPAN></P>
<P>但如果你一开始就有意识使用了工厂模式<SPAN lang=EN-US>,这些麻烦就没有了.</SPAN></P>
<P><STRONG>工厂方法<BR></STRONG>你会建立一个专门生产<SPAN lang=EN-US>Sample实例的工厂:</SPAN></P>
<TABLE style="WIDTH: 80%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellSpacing=3 cellPadding=0 width="80%" border=0>
<TBODY>
<TR>
<TD style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
<P><SPAN lang=EN-US>public class Factory{</SPAN></P>
<P>　　<SPAN lang=EN-US>public static Sample creator(int which){</SPAN></P>
<P>　　//getClass <SPAN lang=EN-US>产生Sample 一般可使用动态类装载装入类。<BR>　　if (which==1)<BR>　　　　return new SampleA();<BR>　　else if (which==2)<BR>　　　　return new SampleB();</SPAN></P>
<P>　　<SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P></TD></TR></TBODY></TABLE>
<P>那么在你的程序中<SPAN lang=EN-US>,如果要实例化Sample时.就使用</SPAN></P>
<P><SPAN lang=EN-US>Sample sampleA=Factory.creator(1);</SPAN></P>
<P>这样<SPAN lang=EN-US>,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.</SPAN></P>
<P>使用工厂方法 要注意几个角色，首先你要定义产品接口，如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类，用来生成产品Sample，如下图，最右边是生产的对象Sample：</P>
<P><IMG height=178 src="http://www.jdon.com/designpatterns/images/factory.jpg" width=526></P>
<P>进一步稍微复杂一点，就是在工厂类上进行拓展，工厂类也有继承它的实现类concreteFactory了<B><I>。</I></B></P>
<P><SPAN lang=EN-US><STRONG>抽象工厂</STRONG><BR>工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).</SPAN></P>
<P style="MARGIN-BOTTOM: 12pt"><SPAN lang=EN-US>这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.</SPAN></P>
<P style="MARGIN-BOTTOM: 12pt">这里假设：Sample有两个concrete类SampleA和SamleB，而Sample2也有两个concrete类Sample2A和SampleB2</P>
<P style="MARGIN-BOTTOM: 12pt"><SPAN lang=EN-US>那么，我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现，下面就是将上例中的Factory拓展成抽象工厂:</SPAN></P>
<TABLE style="WIDTH: 80%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellSpacing=3 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
<P><SPAN lang=EN-US>public abstract class Factory{</SPAN></P>
<P>　　<SPAN lang=EN-US>public abstract Sample creator();</SPAN></P>
<P>　　<SPAN lang=EN-US>public abstract Sample2 creator(String name); </SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US>public class SimpleFactory extends Factory{</SPAN></P>
<P>　　<SPAN lang=EN-US>public Sample creator(){<BR>　　　　.........<BR>　　<SPAN lang=EN-US></SPAN>　　<SPAN lang=EN-US></SPAN>return new SampleA</SPAN><SPAN lang=EN-US><BR>　　}</SPAN></P>
<P>　　<SPAN lang=EN-US>public Sample2 creator(String name){<BR>　　　　.........<BR>　　<SPAN lang=EN-US></SPAN>　　<SPAN lang=EN-US></SPAN>return new Sample2A</SPAN><SPAN lang=EN-US></SPAN><SPAN lang=EN-US><BR>　　}</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US>public class BombFactory extends Factory{</SPAN></P>
<P>　　<SPAN lang=EN-US>public Sample creator(){<BR>　　　　......<BR>　　<SPAN lang=EN-US></SPAN>　　<SPAN lang=EN-US></SPAN>return new SampleB</SPAN><SPAN lang=EN-US></SPAN><SPAN lang=EN-US> <BR>　　}</SPAN></P>
<P>　　<SPAN lang=EN-US>public Sample2 creator(String name){<BR>　　　　......<BR>　　<SPAN lang=EN-US></SPAN>　　<SPAN lang=EN-US></SPAN>return new Sample2B<BR>　　}</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P></TD></TR></TBODY></TABLE>
<P><SPAN lang=EN-US>从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问，为什么我不可以使用两个工厂方法来分别生产Sample和Sample2? </SPAN></P>
<P><SPAN lang=EN-US>抽象工厂还有另外一个关键要点，是因为 SimpleFactory内，生产Sample和生产Sample2的方法之间有一定联系，所以才要将这两个方法捆绑在一个类中，这个工厂类有其本身特征，也许制造过程是统一的，比如：制造工艺比较简单，所以名称叫SimpleFactory。</SPAN><SPAN lang=EN-US><BR></SPAN></P>
<P>在实际应用中，工厂方法用得比较多一些，而且是和动态类装入器组合在一起应用，</P>
<P><SPAN lang=EN-US><STRONG>举例</STRONG><?XML:NAMESPACE PREFIX = O /><O:P></O:P></SPAN></P>
<P>我们以<SPAN lang=EN-US>Jive的ForumFactory为例，这个例子在前面的Singleton模式中我们讨论过，现在再讨论其工厂模式:</SPAN></P>
<TABLE style="WIDTH: 97%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellSpacing=3 cellPadding=0 width="97%" border=0>
<TBODY>
<TR>
<TD style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
<P><SPAN lang=EN-US>public abstract class ForumFactory {</SPAN></P>
<P>　　<SPAN lang=EN-US>private static Object initLock = new Object();<BR>　　private static String className = "com.jivesoftware.forum.database.DbForumFactory";<BR>　　private static ForumFactory factory = null; </SPAN></P>
<P>　　<SPAN lang=EN-US>public static ForumFactory getInstance(Authorization authorization) {<BR>　　　　//If no valid authorization passed in, return null.<BR>　　　　if (authorization == null) {<BR>　　　　　　return null;<BR>　　　　}<BR>　　　　//以下使用了Singleton 单态模式<BR>　　　　if (factory == null) {<BR>　　　　　　synchronized(initLock) {<BR>　　　　　　　　if (factory == null) {<BR>　　　　　　　　　　　　...... </SPAN></P>
<P>　　　　　　　　　　<SPAN lang=EN-US>try {<BR>　　　　　　　　　　　　　　//动态转载类<BR>　　　　　　　　　　　　　　Class c = Class.forName(className);<BR>　　　　　　　　　　　　　　factory = (ForumFactory)c.newInstance();<BR>　　　　　　　　　　}<BR>　　　　　　　　　　catch (Exception e) {<BR>　　　　　　　　　　　　　　return null;<BR>　　　　　　　　　　}<BR>　　　　　　　　}<BR>　　　　　　}<BR>　　　　}</SPAN></P>
<P>　　　　<SPAN lang=EN-US>//Now, 返回 proxy.用来限制授权对forum的访问<BR>　　　　return new ForumFactoryProxy(authorization, factory,<BR>　　　　　　　　　　　　　　　　　　　　factory.getPermissions(authorization));<BR>　　}</SPAN></P>
<P>　　<SPAN lang=EN-US>//真正创建forum的方法由继承forumfactory的子类去完成.<BR>　　public abstract Forum createForum(String name, String description)<BR>　　throws UnauthorizedException, ForumAlreadyExistsException;</SPAN></P>
<P>　　<SPAN lang=EN-US>....</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P></TD></TR></TBODY></TABLE>
<P>因为现在的<SPAN lang=EN-US>Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:</SPAN></P>
<P><SPAN lang=EN-US>private static String className = "com.jivesoftware.forum.database.DbForumFactory";</SPAN></P>
<P>你可以使用自己开发的创建<SPAN lang=EN-US>forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.</SPAN></P>
<P>在上面的一段代码中一共用了三种模式<SPAN lang=EN-US>,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果. &nbsp;</SPAN></P>
<P>看看<SPAN lang=EN-US>Java宠物店中的CatalogDAOFactory:</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>public class CatalogDAOFactory { 
<P></P>
<P>　　/**</P>
<P>　　* 本方法制定一个特别的子类来实现DAO模式。<BR>　　* 具体子类定义是在J2EE的部署描述器中。<BR>　　*/</P>
<P>　　public static CatalogDAO getDAO() throws CatalogDAOSysException {</P>
<P>　　　　CatalogDAO catDao = null;</P>
<P>　　　　try {</P>
<P>　　　　　　InitialContext ic = new InitialContext();<BR>　　　　　　//动态装入CATALOG_DAO_CLASS<BR>　　　　　　//可以定义自己的CATALOG_DAO_CLASS，从而在无需变更太多代码<BR>　　　　　　//的前提下，完成系统的巨大变更。</P>
<P>　　　　　　String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);</P>
<P>　　　　　　catDao = (CatalogDAO) Class.forName(className).newInstance();</P>
<P>　　　　} catch (NamingException ne) {</P>
<P>　　　　　　throw new CatalogDAOSysException("<BR>　　　　　　　　CatalogDAOFactory.getDAO: NamingException while <BR>　　　　　　　　　　getting DAO type : \n" + ne.getMessage());</P>
<P>　　　　} catch (Exception se) {</P>
<P>　　　　　　throw new CatalogDAOSysException("<BR>　　　　　　　　CatalogDAOFactory.getDAO: Exception while getting <BR>　　　　　　　　　　DAO type : \n" + se.getMessage());</P>
<P>　　　　}</P>
<P>　　　　return catDao;</P>
<P>　　}</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P><BR style="mso-ignore: vglayout" clear=all></P>
<P><SPAN lang=EN-US>CatalogDAOFactory是典型的工厂方法，catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类，这个实现子类在Java宠物店是用来操作catalog数据库，用户可以根据数据库的类型不同，定制自己的具体实现子类，将自己的子类名给与CATALOG_DAO_CLASS变量就可以。</SPAN></P>
<P>由此可见，工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制，只要我们更换一下具体的工厂方法，系统其他地方无需一点变换，就有可能将系统功能进行改头换面的变化。</P><img src ="http://www.blogjava.net/fish001118/aggbug/31561.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fish001118/" target="_blank">飞翔天空</a> 2006-02-19 22:23 <a href="http://www.blogjava.net/fish001118/archive/2006/02/19/31561.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新的博客，从这里开始！</title><link>http://www.blogjava.net/fish001118/archive/2006/02/19/31559.html</link><dc:creator>飞翔天空</dc:creator><author>飞翔天空</author><pubDate>Sun, 19 Feb 2006 14:19:00 GMT</pubDate><guid>http://www.blogjava.net/fish001118/archive/2006/02/19/31559.html</guid><wfw:comment>http://www.blogjava.net/fish001118/comments/31559.html</wfw:comment><comments>http://www.blogjava.net/fish001118/archive/2006/02/19/31559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fish001118/comments/commentRss/31559.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fish001118/services/trackbacks/31559.html</trackback:ping><description><![CDATA[<P>我懒得去写什么人生感悟、生活趣事。说实在的，也没空去想。我的博客无非是为了记录一些平素在网上搜到的技术资料，以便自己查阅，也和大家一起分享。之前在CSDN上有一个自己的博客。没什么理由，就想换到这边来。。。。。。也可能是想找个阅读量大的地儿，呵呵！就从今天开始吧！</P><img src ="http://www.blogjava.net/fish001118/aggbug/31559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fish001118/" target="_blank">飞翔天空</a> 2006-02-19 22:19 <a href="http://www.blogjava.net/fish001118/archive/2006/02/19/31559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>