﻿<?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-j2ee绿洲-文章分类-Design Pattern</title><link>http://www.blogjava.net/livery/category/23500.html</link><description>找到属于自己的一片天空</description><language>zh-cn</language><lastBuildDate>Sun, 01 Jul 2007 08:36:27 GMT</lastBuildDate><pubDate>Sun, 01 Jul 2007 08:36:27 GMT</pubDate><ttl>60</ttl><item><title>Factory Pattern</title><link>http://www.blogjava.net/livery/articles/127027.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 29 Jun 2007 03:33:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127027.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127027.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127027.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127027.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127027.html</trackback:ping><description><![CDATA[<p><strong><em>工厂模式定义<span>:提供创建对象的接口.</span></em></strong></p>
<p><strong><em>为何使用<span>?</span></em></strong><span><br>工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式，工厂模式在Java程序系统可以说是随处可见。</span></p>
<p>为什么工厂模式是如此常用？因为工厂模式就相当于创建实例对象的<span>new，我们经常要根据类Class生成实例对象，如A a=new A() 工厂模式也是用来创建实例对象的，所以以后new时就要多个心眼，是否可以考虑实用工厂模式，虽然这样做，可能多做一些工作，但会给你系统带来更大的可扩展性和尽量少的修改量。</span></p>
<p>我们以类<span>Sample为例， 如果我们要创建Sample的实例对象:</span></p>
<p><span>Sample sample=new Sample();</span></p>
<p>可是，实际情况是，通常我们都要在创建<span>sample实例时做点初始化的工作,比如赋值 查询数据库等。</span></p>
<p>首先，我们想到的是，可以使用<span>Sample的构造函数，这样生成实例就写成:</span></p>
<p><span>Sample sample=new Sample(参数);</span></p>
<p>但是，如果创建<span>sample实例时所做的初始化工作不是象赋值这样简单的事，可能是很长一段代码，如果也写入构造函数中，那你的代码很难看了（就需要Refactor重整）。</span></p>
<p>为什么说代码很难看，初学者可能没有这种感觉，我们分析如下，初始化工作如果是很长一段代码，说明要做的工作很多，将很多工作装入一个方法中，相当于将很多鸡蛋放在一个篮子里，是很危险的，这也是有背于<span>Java面向对象的原则，面向对象的封装(Encapsulation)和分派(Delegation)告诉我们，尽量将长的代码分派&#8220;切割&#8221;成每段，将每段再&#8220;封装&#8221;起来(减少段和段之间偶合联系性)，这样，就会将风险分散，以后如果需要修改，只要更改每段，不会再发生牵一动百的事情。</span></p>
<p>在本例中，首先，我们需要将创建实例的工作与使用实例的工作分开<span>, 也就是说，让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。</span></p>
<p>这时我们就需要<span>Factory工厂模式来生成对象了，不能再用上面简单new Sample(参数)。</span>还有<span>,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.</span>现在<span>Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:</span></p>
<p><span>Sample mysample=new MySample();<br>Sample hissample=new HisSample();</span></p>
<p>随着项目的深入<span>,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.</span></p>
<p>但如果你一开始就有意识使用了工厂模式<span>,这些麻烦就没有了.</span></p>
<p><strong>工厂方法<br></strong>你会建立一个专门生产<span>Sample实例的工厂:</span></p>
<table cellSpacing=3 cellPadding=0 width="80%" border=0>
    <tbody>
        <tr>
            <td>
            <p><span>public class Factory{</span></p>
            <p>　　<span>public static Sample creator(int which){</span></p>
            <p>　　//getClass <span>产生Sample 一般可使用动态类装载装入类。<br>　　if (which==1)<br>　　　　return new SampleA();<br>　　else if (which==2)<br>　　　　return new SampleB();</span></p>
            <p>　　<span>}</span></p>
            <p><span>}</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>那么在你的程序中<span>,如果要实例化Sample时.就使用</span></p>
<p><span>Sample sampleA=Factory.creator(1);</span></p>
<p>这样<span>,在整个就不涉及到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了<strong><em>。</em></strong></p>
<p><span><strong>抽象工厂</strong><br>工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).</span></p>
<p><span>这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.</span></p>
<p>这里假设：Sample有两个concrete类SampleA和SamleB，而Sample2也有两个concrete类Sample2A和SampleB2</p>
<p><span>那么，我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现，下面就是将上例中的Factory拓展成抽象工厂:</span></p>
<table cellSpacing=3 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <p><span>public abstract class Factory{</span></p>
            <p>　　<span>public abstract Sample creator();</span></p>
            <p>　　<span>public abstract Sample2 creator(String name); </span></p>
            <p><span>}</span></p>
            <p><span>public class SimpleFactory extends Factory{</span></p>
            <p>　　<span>public Sample creator(){<br>　　　　.........<br>　　　　return new SampleA</span><span><br>　　}</span></p>
            <p>　　<span>public Sample2 creator(String name){<br>　　　　.........<br>　　　　return new Sample2A</span><span><br>　　}</span></p>
            <p><span>}</span></p>
            <p><span>public class BombFactory extends Factory{</span></p>
            <p>　　<span>public Sample creator(){<br>　　　　......<br>　　　　return new SampleB</span><span> <br>　　}</span></p>
            <p>　　<span>public Sample2 creator(String name){<br>　　　　......<br>　　　　return new Sample2B<br>　　}</span></p>
            <p><span>}</span></p>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问，为什么我不可以使用两个工厂方法来分别生产Sample和Sample2? </span></p>
<p><span>抽象工厂还有另外一个关键要点，是因为 SimpleFactory内，生产Sample和生产Sample2的方法之间有一定联系，所以才要将这两个方法捆绑在一个类中，这个工厂类有其本身特征，也许制造过程是统一的，比如：制造工艺比较简单，所以名称叫SimpleFactory。</span><span><br></span></p>
<p>在实际应用中，工厂方法用得比较多一些，而且是和动态类装入器组合在一起应用，</p>
<p><span><strong>举例</strong></span></p>
<p>我们以<span>Jive的ForumFactory为例，这个例子在前面的Singleton模式中我们讨论过，现在再讨论其工厂模式:</span></p>
<table cellSpacing=3 cellPadding=0 width="97%" border=0>
    <tbody>
        <tr>
            <td>
            <p><span>public abstract class ForumFactory {</span></p>
            <p>　　<span>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>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>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>//Now, 返回 proxy.用来限制授权对forum的访问<br>　　　　return new ForumFactoryProxy(authorization, factory,<br>　　　　　　　　　　　　　　　　　　　　factory.getPermissions(authorization));<br>　　}</span></p>
            <p>　　<span>//真正创建forum的方法由继承forumfactory的子类去完成.<br>　　public abstract Forum createForum(String name, String description)<br>　　throws UnauthorizedException, ForumAlreadyExistsException;</span></p>
            <p>　　<span>....</span></p>
            <p><span>}</span></p>
            <p>&nbsp;</p>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p>因为现在的<span>Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:</span></p>
<p><span>private static String className = "com.jivesoftware.forum.database.DbForumFactory";</span></p>
<p>你可以使用自己开发的创建<span>forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.</span></p>
<p>在上面的一段代码中一共用了三种模式<span>,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果. &nbsp;</span></p>
<p>看看<span>Java宠物店中的CatalogDAOFactory:</span></p>
<table cellSpacing=0 cellPadding=0 width="100%" bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td>public class CatalogDAOFactory {
            <p>&#160;</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 clear=all></p>
<p><span>CatalogDAOFactory是典型的工厂方法，catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类，这个实现子类在Java宠物店是用来操作catalog数据库，用户可以根据数据库的类型不同，定制自己的具体实现子类，将自己的子类名给与CATALOG_DAO_CLASS变量就可以。</span></p>
<p>由此可见，工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制，只要我们更换一下具体的工厂方法，系统其他地方无需一点变换，就有可能将系统功能进行改头换面的变化。</p>
<img src ="http://www.blogjava.net/livery/aggbug/127027.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-06-29 11:33 <a href="http://www.blogjava.net/livery/articles/127027.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Proxy</title><link>http://www.blogjava.net/livery/articles/127024.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 29 Jun 2007 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127024.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127024.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127024.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127024.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127024.html</trackback:ping><description><![CDATA[<p>理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣.</p>
<p>代理模式是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.</p>
<p><strong>设计模式中定义</strong>: 为其他对象提供一种代理以控制对这个对象的访问.</p>
<p><strong>为什么要使用Proxy?</strong><br>1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.</p>
<p>2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.<br>举例两个具体情况: <br>(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.<br><br>(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.<br></p>
<p>总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.</p>
<p><strong>如何使用Proxy?</strong><br>以<a href="http://www.jdon.com/dl/jive.zip"><u><font color=#0000ff>Jive论坛系统</font></u></a>为例,访问论坛系统的用户有多种类型:注册普通用户 论坛管理者 系统管理者 游客,注册普通用户才能发言;论坛管理者可以管理他被授权的论坛;系统管理者可以管理所有事务等,这些权限划分和管理是使用Proxy完成的.</p>
<p>Forum是Jive的核心接口,在Forum中陈列了有关论坛操作的主要行为,如论坛名称 论坛描述的获取和修改,帖子发表删除编辑等.</p>
<p>在ForumPermissions中定义了各种级别权限的用户:</p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public class ForumPermissions implements Cacheable {
            <p>/**<br>* Permission to read object.<br>*/<br>public static final int READ = 0;</p>
            <p>/**<br>* Permission to administer the entire sytem.<br>*/<br>public static final int SYSTEM_ADMIN = 1;</p>
            <p>/**<br>* Permission to administer a particular forum.<br>*/<br>public static final int FORUM_ADMIN = 2;</p>
            <p>/**<br>* Permission to administer a particular user.<br>*/<br>public static final int USER_ADMIN = 3;</p>
            <p>/**<br>* Permission to administer a particular group.<br>*/<br>public static final int GROUP_ADMIN = 4;</p>
            <p>/**<br>* Permission to moderate threads.<br>*/<br>public static final int MODERATE_THREADS = 5;</p>
            <p>/**<br>* Permission to create a new thread.<br>*/<br>public static final int CREATE_THREAD = 6;</p>
            <p>/**<br>* Permission to create a new message.<br>*/<br>public static final int CREATE_MESSAGE = 7;</p>
            <p>/**<br>* Permission to moderate messages.<br>*/<br>public static final int MODERATE_MESSAGES = 8;</p>
            <p>.....</p>
            <p>public boolean isSystemOrForumAdmin() {<br>　　return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]);<br>}</p>
            <p>.....</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来.比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下:</p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class ForumProxy implements Forum {</p>
            <p>private ForumPermissions permissions;<br>private Forum forum; <br>this.authorization = authorization; <br><br>public ForumProxy(Forum forum, Authorization authorization,<br>ForumPermissions permissions)<br>{<br>this.forum = forum;<br>this.authorization = authorization;<br>this.permissions = permissions;<br>}<br><br>.....</p>
            <p>public void setName(String name) throws UnauthorizedException,<br>ForumAlreadyExistsException<br>{<br>　　//只有是系统或论坛管理者才可以修改名称<br>　　if (permissions.isSystemOrForumAdmin()) {<br>　　　　forum.setName(name);<br>　　}<br>　　else {<br>　　　　throw new UnauthorizedException();<br>　　}<br>}</p>
            <p>...</p>
            <p>}<br></p>
            </td>
        </tr>
    </tbody>
</table>
<p>而DbForum才是接口Forum的真正实现,以修改论坛名称为例:</p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class DbForum implements Forum, Cacheable {<br>...</p>
            <p>public void setName(String name) throws ForumAlreadyExistsException {<br><br>　　....<br><br>　　this.name = name;<br>　　//这里真正将新名称保存到数据库中 <br>　　saveToDb();</p>
            <p>　　....<br>}<br></p>
            <p><br>... </p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的"网关","安全代理系统".</p>
<p>在平时应用中,无可避免总要涉及到系统的授权或安全体系,不管你有无意识的使用Proxy,实际你已经在使用Proxy了.</p>
<p>我们继续结合Jive谈入深一点,下面要涉及到工厂模式了,如果你不了解工厂模式,请看我的另外一篇文章:<a href="http://www.jdon.com/designpatterns/designpattern_factory.htm" target=_blank><u><font color=#800080>设计模式之Factory</font></u></a></p>
<p>我们已经知道,使用Forum需要通过ForumProxy,Jive中创建一个Forum是使用Factory模式,有一个总的抽象类ForumFactory,在这个抽象类中,调用ForumFactory是通过getInstance()方法实现,这里使用了Singleton(也是设计模式之一,由于介绍文章很多,我就不写了,<a href="http://www-900.ibm.com/developerWorks/java/designpattern/singleton/index.shtml" target=_blank><u><font color=#0000ff>看这里</font></u></a>),getInstance()返回的是ForumFactoryProxy.</p>
<p>为什么不返回ForumFactory,而返回ForumFactory的实现ForumFactoryProxy?<br>原因是明显的,需要通过代理确定是否有权限创建forum.</p>
<p>在ForumFactoryProxy中我们看到代码如下:</p>
<table cellSpacing=3 cellPadding=3 width="98%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public class ForumFactoryProxy extends ForumFactory {
            <p>　　protected ForumFactory factory;<br>　　protected Authorization authorization;<br>　　protected ForumPermissions permissions;</p>
            <p>　　public ForumFactoryProxy(Authorization authorization, ForumFactory factory,<br>　　ForumPermissions permissions)<br>　　{<br>　　　　this.factory = factory;<br>　　　　this.authorization = authorization;<br>　　　　this.permissions = permissions;<br>　　}</p>
            <p>　　public Forum createForum(String name, String description)<br>　　　　　　throws UnauthorizedException, ForumAlreadyExistsException<br>　　{<br>　　　　//只有系统管理者才可以创建forum <br>　　　　if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {<br>　　　　　　Forum newForum = factory.createForum(name, description);<br>　　　　　　return new ForumProxy(newForum, authorization, permissions);<br>　　　　}<br>　　　　else {<br>　　　　　　throw new UnauthorizedException();<br>　　}<br>}<br></p>
            </td>
        </tr>
    </tbody>
</table>
<p>方法createForum返回的也是ForumProxy, Proxy就象一道墙,其他程序只能和Proxy交互操作.</p>
<p>注意到这里有两个Proxy:ForumProxy和ForumFactoryProxy. 代表两个不同的职责:使用Forum和创建Forum;<br>至于为什么将使用对象和创建对象分开,这也是为什么使用Factory模式的原因所在:是为了"封装" "分派";换句话说,尽可能功能单一化,方便维护修改.</p>
<p>Jive论坛系统中其他如帖子的创建和使用,都是按照Forum这个思路而来的.</p>
<p>以上我们讨论了如何使用Proxy进行授权机制的访问,Proxy还可以对用户隐藏另外一种称为copy-on-write的优化方式.拷贝一个庞大而复杂的对象是一个开销很大的操作,如果拷贝过程中,没有对原来的对象有所修改,那么这样的拷贝开销就没有必要.用代理延迟这一拷贝过程.</p>
<p>比如:我们有一个很大的Collection,具体如hashtable,有很多客户端会并发同时访问它.其中一个特别的客户端要进行连续的数据获取,此时要求其他客户端不能再向hashtable中增加或删除 东东.</p>
<p>最直接的解决方案是:使用collection的lock,让这特别的客户端获得这个lock,进行连续的数据获取,然后再释放lock.<br>public void foFetches(Hashtable ht){<br>　　synchronized(ht){<br>　　　　//具体的连续数据获取动作.. <br>　　} </p>
<p>}<br></p>
<p>但是这一办法可能锁住Collection会很长时间,这段时间,其他客户端就不能访问该Collection了.</p>
<p>第二个解决方案是clone这个Collection,然后让连续的数据获取针对clone出来的那个Collection操作.这个方案前提是,这个Collection是可clone的,而且必须有提供深度clone的方法.Hashtable就提供了对自己的clone方法,但不是Key和value对象的clone,关于Clone含义可以参考<a href="http://www-900.ibm.com/developerWorks/java/l-jpointer/index.shtml" target=_blank><u><font color=#0000ff>专门文章</font></u></a>.<br>public void foFetches(Hashtable ht){<br></p>
<p>　　Hashttable newht=(Hashtable)ht.clone();</p>
<p>}</p>
<p>问题又来了,由于是针对clone出来的对象操作,如果原来的母体被其他客户端操作修改了, 那么对clone出来的对象操作就没有意义了.</p>
<p>最后解决方案:我们可以等其他客户端修改完成后再进行clone,也就是说,这个特别的客户端先通过调用一个叫clone的方法来进行一系列数据获取操作.但实际上没有真正的进行对象拷贝,直至有其他客户端修改了这个对象Collection.</p>
<p>使用Proxy实现这个方案.这就是copy-on-write操作.</p>
<p>Proxy应用范围很广,现在流行的分布计算方式RMI和Corba等都是Proxy模式的应用.</p>
<img src ="http://www.blogjava.net/livery/aggbug/127024.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-06-29 11:30 <a href="http://www.blogjava.net/livery/articles/127024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Command Pattern</title><link>http://www.blogjava.net/livery/articles/125764.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 22 Jun 2007 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/125764.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/125764.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/125764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/125764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/125764.html</trackback:ping><description><![CDATA[<p>Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体,规定很多的模式,正是这个灵活性,让人有些confuse.</p>
<p><strong>Command定义</strong><br>n 将来自客户端的请求传入一个对象，无需了解这个请求激活的 动作或有关接受这个请求的处理细节。</p>
<p>这是一种两台机器之间通讯联系性质的模式，类似传统过程语 言的 CallBack功能。 </p>
<p><strong>优点：<br></strong>解耦了发送者和接受者之间联系。 发送者调用一个操作，接受者接受请求执行相应的动作，因为使用Command模式解耦，发送者无需知道接受者任何接口。</p>
<p>不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.</p>
<p>将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.</p>
<p>显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,<br>从Command模式,我也发现设计模式一个"通病":好象喜欢将简单的问题复杂化, 喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性 可维护性 还有复用性.</p>
<p><strong>如何使用?</strong><br>具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:</p>
典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":<br>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public interface Command {<br>　　public abstract void execute ( );<br>}</td>
        </tr>
    </tbody>
</table>
<br>具体不同命令/请求代码是实现接口Command,下面有三个具体命令<br>
<table cellSpacing=3 cellPadding=3 width="80%" bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td>public class Engineer implements Command {<br><br>　　public void execute( ) {<br>　　　　//do Engineer's command<br>　　}<br>}
            <p>public class Programmer implements Command {<br><br>　　public void execute( ) {<br>　　　　//do programmer's command<br>　　}<br>}</p>
            <p>public class Politician implements Command {<br><br>　　public void execute( ) {<br>　　　　//do Politician's command<br>　　}<br>}</p>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去:<br></p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class producer{<br>　　public static List produceRequests() {<br>　　　　List queue = new ArrayList();<br>　　　　queue.add( new DomesticEngineer() );<br>　　　　queue.add( new Politician() );<br>　　　　queue.add( new Programmer() );<br>　　　　return queue; <br>　　}</p>
            <p>}</p>
            <p>&#160;</p>
            </td>
        </tr>
    </tbody>
</table>
<p>这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面客户端如何调用Command模式:<br></p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class TestCommand {<br>　　public static void main(String[] args) {<br>　　　　<br>　　　　List queue = Producer.produceRequests();<br>　　　　for (Iterator it = queue.iterator(); it.hasNext(); )<br>　　　　　　<br>　//客户端直接调用execute方法，无需知道被调用者的其它更多类的方法名。<br>　　　　　　　　((Command)it.next()).execute();<br>　　</p>
            <p>　　}<br>} </p>
            </td>
        </tr>
    </tbody>
</table>
<p>由此可见,调用者基本只和接口打交道,不合具体实现交互,这也体现了一个原则,面向接口编程,这样,以后增加第四个具体命令时,就不必修改调用者TestCommand中的代码了.<br><br>理解了上面的代码的核心原理,在使用中,就应该各人有自己方法了,特别是在如何分离调用者和具体命令上,有很多实现方法,上面的代码是使用"从List过一遍"的做法.这种做法只是为了演示.<br></p>
<p>使用Command模式的一个好理由还因为它能实现Undo功能.每个具体命令都可以记住它刚刚执行的动作,并且在需要时恢复.</p>
<p>Command模式在界面设计中应用广泛.Java的Swing中菜单命令都是使用Command模式,由于Java在界面设计的性能上还有欠缺,因此界面设计具体代码我们就不讨论,网络上有很多这样的示例.</p>
<img src ="http://www.blogjava.net/livery/aggbug/125764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-06-22 10:55 <a href="http://www.blogjava.net/livery/articles/125764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Strategy Pattern</title><link>http://www.blogjava.net/livery/articles/125763.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 22 Jun 2007 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/125763.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/125763.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/125763.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/125763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/125763.html</trackback:ping><description><![CDATA[<p>Strategy策略模式是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类.</p>
<p>Stratrgy应用比较广泛,比如, 公司经营业务变化图, 可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现.</p>
<p>这里以字符串替代为例, 有一个文件,我们需要读取后,希望替代其中相应的变量,然后输出.关于替代其中变量的方法可能有多种方法,这取决于用户的要求,所以我们要准备几套变量字符替代方案.</p>
<p><img height=323 src="http://www.jdon.com/designpatterns/images/Strategy.jpg" width=443></p>
<p>&nbsp;</p>
<p>首先,我们建立一个抽象类RepTempRule 定义一些公用变量和方法:</p>
<table borderColor=#cccccc cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public abstract class RepTempRule{<br><br>protected String oldString="";<br>public void setOldString(String oldString){<br>　　this.oldString=oldString; <br>}<br><br>protected String newString="";<br>public String getNewString(){<br>　　return newString;<br>}<br><br><br><br>public abstract void replace() throws Exception;<br><br><br>}</td>
        </tr>
    </tbody>
</table>
<p>在RepTempRule中 有一个抽象方法abstract需要继承明确,这个replace里其实是替代的具体方法.<br>我们现在有两个字符替代方案,<br>1.将文本中aaa替代成bbb;<br>2.将文本中aaa替代成ccc;<br><br>对应的类分别是RepTempRuleOne RepTempRuleTwo</p>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class RepTempRuleOne extends RepTempRule{<br><br><br>public void replace() throws Exception{ <br><br>　　//replaceFirst是jdk1.4新特性 <br>　　newString=oldString.replaceFirst("aaa", "bbbb") <br>　　System.out.println("this is replace one");<br>　　 <br>}<br><br><br>}</p>
            </td>
        </tr>
    </tbody>
</table>
<table cellSpacing=3 cellPadding=3 width="80%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public class RepTempRuleTwo extends RepTempRule{<br><br><br>public void replace() throws Exception{ <br><br>　　newString=oldString.replaceFirst("aaa", "ccc") <br>　　System.out.println("this is replace Two");<br>　　 <br>}<br><br><br>}</td>
        </tr>
    </tbody>
</table>
<p>第二步：我们要建立一个算法解决类，用来提供客户端可以自由选择算法。</p>
<table width="80%" border=1>
    <tbody>
        <tr>
            <td bgColor=#cccccc>public class RepTempRuleSolve {
            <p>　　private RepTempRule strategy;</p>
            <p>　　public RepTempRuleSolve(RepTempRule rule){<br>　　　　this.strategy=rule;<br>　　}</p>
            <p>　　public String getNewContext(Site site,String oldString) {<br>　　　　return strategy.replace(site,oldString);<br>　　}</p>
            <p>　　public void changeAlgorithm(RepTempRule newAlgorithm) {<br>　　　　strategy = newAlgorithm;<br>　　}</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>调用如下:</p>
<table cellSpacing=3 cellPadding=3 width="100%" border=0>
    <tbody>
        <tr>
            <td bgColor=#cccccc>
            <p>public class test{</p>
            <p>......</p>
            <p>　　public void testReplace(){</p>
            <p>　　//使用第一套替代方案<br>　　RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple());<br>　　solver.getNewContext(site,context); </p>
            <p>　　//使用第二套</p>
            <p>　　solver=new RepTempRuleSolve(new RepTempRuleTwo());<br>　　solver.getNewContext(site,context); </p>
            <p>&#160;</p>
            <p>　　}</p>
            <p>.....</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>我们达到了在运行期间，可以自由切换算法的目的。</p>
<p>实际整个Strategy的核心部分就是抽象类的使用,使用Strategy模式可以在用户需要变化时,修改量很少,而且快速.</p>
<p>Strategy和Factory有一定的类似,Strategy相对简单容易理解,并且可以在运行时刻自由切换。Factory重点是用来创建对象。</p>
<p>Strategy适合下列场合:</p>
<p>1.以不同的格式保存文件;</p>
<p>2.以不同的算法压缩文件;</p>
<p>3.以不同的算法截获图象;</p>
<p>4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等</p>
<img src ="http://www.blogjava.net/livery/aggbug/125763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-06-22 10:49 <a href="http://www.blogjava.net/livery/articles/125763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>