﻿<?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-细心!用心!耐心!-文章分类-Behavioral 模式 </title><link>http://www.blogjava.net/jesson2005/category/21671.html</link><description>吾非文人，乃市井一俗人也，读百卷书，跨江河千里，故申城一游； 
一两滴辛酸，三四年学业，五六点粗墨，七八笔买卖，九十道人情。</description><language>zh-cn</language><lastBuildDate>Wed, 18 Apr 2007 07:02:31 GMT</lastBuildDate><pubDate>Wed, 18 Apr 2007 07:02:31 GMT</pubDate><ttl>60</ttl><item><title>Design Pattern: Visitor 模式</title><link>http://www.blogjava.net/jesson2005/articles/111193.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111193.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111193.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111193.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111193.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111193.html</trackback:ping><description><![CDATA[在Java中所有的物件都繼承自Object物件，這樣作的優點之一，就是使得一些集合物件的資料結構容易管理，例如您可以將任何型態的物件放入Vector中。<br><br>然而現在有個問題是，如果您的集合（connection）物件中不僅儲存一種型態的物件，如果想要對這些物件作出一些個別化的操作，首要條件就是要知道該物件的型態，使用 instanceof 似乎是個不錯的方式，在程式簡單的情況下，也許您會這麼作：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class ElementA { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // some implementing </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;} </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class ElementB { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // some implementing </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;} </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class ElementC { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // some implementing </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;} </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;// ...... </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; Iterator iterator = arrayList.iterator() </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; while (iterator.hasNext()) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (o instanceof ElementA) </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　 　　&nbsp;&nbsp; (ElementA) o.operationA(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (o instanceof ElementB) </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　 　&nbsp;&nbsp;&nbsp;&nbsp; (ElementB) o.operationB(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　&nbsp;&nbsp;&nbsp; else if (o instanceof ElementC) </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　 　&nbsp;&nbsp;&nbsp;&nbsp; (ElementC) o.operationC(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　&nbsp;&nbsp;&nbsp; else </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">　　 　&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Sorry! I don't know who you are! " </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + o.toString()); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //....</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; }</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; //....</span><br></div>
&nbsp;<br>這麼作並不是不可以，只是將來的擴充性不大，如果今天您想要一次改變對每一種類型物件的操作，您必須修改很多地方。<br><br>從物件自身的角度來想好了，物件在一個個的房子中，物件說：「不要在房子外費盡心思判斷了，即然您不知道我是誰，那麼您就進來訪問我好了，我告訴您我是誰，這麼一來您就知道如何操作我了！」<br><br>用程式來實現上面這個描述：<br>
<ul>
    <li>IElement.java </li>
</ul>
<pre>public interface IElement { <br>    public void accept(IVisitor visitor); <br>} <br></pre>
<br>
<ul>
    <li>ElementA.java </li>
</ul>
<pre>public class ElementA implements IElement { <br>    public void accept(IVisitor visitor) { <br>        visitor.visit(this); <br>    }<br><br>    public void operationA() { <br>        System.out.println(<br>              "do A's job....such-and-such...."); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>ElementB.java </li>
</ul>
<pre>public class ElementB implements IElement { <br>    public void accept(IVisitor visitor) { <br>        visitor.visit(this); <br>    }<br><br>    public void operationB() { <br>        System.out.println(<br>           "do B's job....such-and-such...."); <br>    }<br>} <br></pre>
<br>
<ul>
    <li>ElementC.java </li>
</ul>
<pre>public class <span class=createlink>ElementC</span> implements <span class=createlink>IElement</span> { <br>    public void accept(<span class=createlink>IVisitor</span> visitor) { <br>        visitor.visit(this); <br>    }<br><br>    public void operationC() { <br>        System.out.println(<br>            "do C's job....such-and-such...."); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>IVisitor.java </li>
</ul>
<pre>public interface IVisitor { <br>    public void visit(ElementA element); <br>    public void visit(ElementB element); <br>    public void visit(ElementC element); <br>}  <br></pre>
<br>
<ul>
    <li>VisitorA.java </li>
</ul>
<pre>public class VisitorA implements IVisitor { <br>    public void visit(ElementA element) { <br>        element.operationA(); <br>    }<br><br>    public void visit(ElementB element) { <br>        element.operationB(); <br>    }<br><br>    public void visit(ElementC element) { <br>        element.operationC(); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        // know nothing about their type <br>        // after storing them into Element array <br>        IElement[] list = {new ElementA(), <br>                           new ElementB(), <br>                           new ElementC()}; <br><br>        IVisitor visitor = new VisitorA();<br><br>        for (int i=0; i &lt; list.length; i++) <br>            list[i].accept(visitor); <br>    } <br>} </pre>
<br>Visitor訪問是基於overload來完成，對於每一個實現IElement的物件來說，它接受IVisitor來訪問它，在accept()方法中，IVisitor使用正確的方法來訪問IElement（顯然的，這麼部份可以靠不同的函式名稱，或是overload來達成），並在visit() 中對IElement作出對應的操作，如果您今天想要換掉每一個IElement的操作，只要更換IVisitor類型的物件就可以了，也就是這行：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;// IVisitor visitor = new VisitorA(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;// 換掉一個IVisitor，就可以換掉所有的操作</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;// 不用修改多個地方</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;IVisitor visitor = new VisitorB(); </span><br></div>
&nbsp;<br><br>舉個實際的例子，假設VisitorA只是個懶惰的推銷員好了，今天有一個比較勤快的推銷員VisitorB，在訪問過IElement之後，會對 IElement作出更多的操作，要在程式中實現VisitorB，只要增加一個VisitorB類別就可以了：<br>
<ul>
    <li>VisitorB.java </li>
</ul>
<pre>public class VisitorB implements IVisitor { <br>    public void visit(ElementA element) { <br>        System.out.println("VisitorB is a hard worker...."); <br>        element.operationA(); <br>        System.out.println(<br>            "I want to do some extra work on A...."); <br>    }<br><br>    public void visit(ElementB element) { <br>        System.out.println("VisitorB is a hard worker...."); <br>        element.operationB(); <br>        System.out.println(<br>                   "I want to do some extra work on B...."); <br>    }<br><br>    public void visit(ElementC element) { <br>        System.out.println("VisitorB is a hard worker...."); <br>        element.operationC(); <br>        System.out.println(<br>                  "I want to do some extra work on C...."); <br>    } <br>} <br></pre>
<br>改一下Main來示範：<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        IElement[] list = {new ElementA(), <br>                           new ElementB(), <br>                           new ElementC()}; <br><br>        System.out.println("visitorA is coming......."); <br>        IVisitor visitorA = new VisitorA(); <br>        for (int i=0; i &lt; list.length; i++) <br>           list[i].accept(visitorA);<br><br>        System.out.println("\nvisitorB is coming......."); <br>        IVisitor visitorB = new VisitorB(); <br>        for (int i=0; i &lt; list.length; i++) <br>            list[i].accept(visitorB); <br>    } <br>} <br></pre>
<br>在範例中的System.out.println();只是個示意，它也可能是您對IElement的額外方法的直接調用。 <br><br>Visitor模式的 UML 結構類圖如下：<br>
<div style="TEXT-ALIGN: center"><img title=Visitor style="WIDTH: 544px; HEIGHT: 430px" alt=Visitor src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/visitor-1.jpg"><br><br></div>
在<a href="http://www.javaworld.com/">Java World</a>中有一篇文章，提到可以利用reflection來改進使用訪問者模式時的彈性，有興趣的可以進一步參考一下<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip98.html">Reflect on the Visitor design pattern</a>。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:53 <a href="http://www.blogjava.net/jesson2005/articles/111193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Strategy 模式</title><link>http://www.blogjava.net/jesson2005/articles/111191.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:52:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111191.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111191.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111191.html</trackback:ping><description><![CDATA[考慮您要設計一個更換各種符號的工具類TextCharChange，您是否會採用這樣的方式：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public void replace() { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp; switch(getChangeType()) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case RN_TYPE:&nbsp;&nbsp; replaceRN(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case N_TYPE: replaceN(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case OTHER_TYPE: replaceOTHER(): </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">}</span><br></div>
&nbsp;<br>這麼作的缺點是，日後您要增加更換符號的策略時，會有幾個地方需要修改：增加TYPE常數、增加TextCharChange中的 replaceXXX()方法、增加 replace()方法中的switch case判斷。<br><br>像這種策略採用的情況，可以將策略加以封裝為一個物件，而不是將策略寫死在某個類中，如此一來，策略可以獨立於客戶端，隨時增加變化、增加或減少策略，即使是修改每個策略的內容，也不會對客戶端程式造成影響。<br><br>來舉個最簡單的例子，首先要知道Windows與Linux的文字檔案換行符號是不同的，Windows是 /r/n ，而Linux是 /n，今天您要設計一個文字編輯器，在適當的時候，您必須要能隨時轉換這兩種符號，如果不採用上面的策略採用流程的話，要如何設計：<br>
<ul>
    <li>TextStrategy.java </li>
</ul>
<pre>public abstract class TextStrategy { <br>    protected String text;<br><br>    public TextStrategy(String text) { <br>        this.text = text; <br>    }<br><br>    public abstract String replace(); <br>}  <br></pre>
<br>
<ul>
    <li>LinuxStrategy.java </li>
</ul>
<pre>public class LinuxStrategy extends TextStrategy { <br>    public LinuxStrategy(String text) { <br>        super(text); <br>    }<br><br>    public String replace() { <br>        preOperation(); <br>        System.out.println(<br>               text = text.replaceAll("@r@n", "@n")); <br>        postOperation(); <br><br>        return text; <br>    }<br><br>    private void preOperation() { <br>        System.out.println("LinuxStrategy preOperation"); <br>    }<br><br>    private void postOperation() { <br>        System.out.println("LinuxStrategy postOperation"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>WindowsStrategy.java </li>
</ul>
<pre>public class WindowsStrategy extends TextStrategy { <br>    public WindowsStrategy(String text) { <br>        super(text); <br>    }<br><br>    public String replace() { <br>        startOperation(); <br>        System.out.println(<br>                     text = text.replaceAll("@n", "@r@n")); <br>        endOperation(); <br><br>        return text; <br>    }<br><br>    private void startOperation() { <br>        System.out.println("WindowsStrategy startOperation"); <br>    } <br><br>    private void endOperation() { <br>        System.out.println("WindowsStrategy endOperation"); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>TextCharChange.java </li>
</ul>
<pre>public class TextCharChange { <br>    public static void replace(TextStrategy strategy) { <br>        strategy.replace(); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        String linuxText = <br>            "This is a test text!!@n Oh! Line Return!!@n"; <br>        String windowsText = <br>            "This is a test text!!@r@n Oh! Line Return@r@n"; <br><br>        // load file, suppose it's Linux's text file <br>        // take the WindowsStrategy <br>        // I want to change it to Windows' text file <br>        TextCharChange.replace(<br>              new WindowsStrategy(linuxText)); <br><br>        // such-and-such operation..... <br>        System.out.println(); <br><br>        // load file, suppose it's Windows' text file <br>        // take the LinuxStrategy <br>        // I want to change it to Linux's text file <br>        TextCharChange.replace(<br>            new LinuxStrategy(windowsText)); <br>    } <br>} <br></pre>
<br>為了明顯的秀出結果，我們使用@n來表示 '/n' ， @r 表示 '/r' 符號，Main中的流程是個假設的情況，何時採用何種策略是隨機的。<br><br>在Strategy模式中，使用一個公開的介面replace()，讓客戶端請求，而在實作replace()時，可以任意的組合演算策略，程式中的 preOperation()、postOperation()就是用以示意演算的組合概念，Strategy模式封裝了這些演算過程，使它們易於組合、修改、替換，上面這個例子的UML 類別結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=Strategy style="WIDTH: 455px; HEIGHT: 151px" alt=Strategy src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/strategy-1.jpg"><br></div>
<br>Strategy模式的UML類別結構圖如下：<br>
<div style="TEXT-ALIGN: center"><img title=Strategy style="WIDTH: 575px; HEIGHT: 168px" alt=Strategy src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/strategy-2.jpg"></div>
從行為上來說，<a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/StatePattern.htm">State 模式</a> 與Strategy模式是蠻相近的。<br><br>State模式：看當前是什麼狀態，就採取什麼動作。<br><br>Strategy模式：看需求（情境）是什麼，採用適當的策略。<br><br>不過兩者雖相似，應用的場合稍有不同，State模式中有一個重點在於設定狀態變化，就像 Gof 例子中舉的TCP連線；Strategy策略模式則是直接採用適當的策略的感覺，例如Gof中說的，採用適當的演算法來作正文換行。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:52 <a href="http://www.blogjava.net/jesson2005/articles/111191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Template Method 模式</title><link>http://www.blogjava.net/jesson2005/articles/111192.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:52:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111192.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111192.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111192.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111192.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111192.html</trackback:ping><description><![CDATA[不要將設計模式想得高不可攀，好像高手才會使用的東西，事實上如果您在下手程式之前，能稍稍對程式作個分析規劃，或多或少都會用到一些模式了，模式不是教條，它只是前人的經驗成果，而 Gof 的書則是擇前人之精華持續改進而來罷了。<br><br>Template Method模式就是一個很簡單的模式，但可能是使用最廣泛的模式，也許您也一直在使用這樣的模式，看它的 UML 類別結構圖就知道了：<br>
<div style="TEXT-ALIGN: center"><img title=TemplateMethod style="WIDTH: 338px; HEIGHT: 205px" alt=TemplateMethod src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/templateMethod-1.jpg"><br></div>
<br>僅僅是抽象類別與具體類別實作的關係而已，有些人常問抽象類別與介面的區別為何，Template Method模式可以提供其中一個答案，例如：<br>
<ul>
    <li>AbstractClass.java </li>
</ul>
<pre>public abstract class AbstractClass { <br>    public void templateMethod() { <br>        // step by step template to solve something <br>        // implementor should follow those step <br>        opStep1(); <br>        opStep2(); <br>        opStep3(); <br>    } <br><br>    public abstract void opStep1(); <br>    public abstract void opStep2(); <br>    public abstract void opStep3(); <br>} <br></pre>
<br>
<ul>
    <li>ConcreteClass.java </li>
</ul>
<pre>public class ConcreteClass extends AbstractClass { <br>    public abstract void opStep1() { <br>        // implement the real operation <br>    } <br><br>    public abstract void opStep2() { <br>        // implement the real operation <br>    } <br><br>    public abstract void opStep3() { <br>        // implement the real operation <br>    } <br>}</pre>
<br>對於一些程式而言，我們希望規定一些處理的步驟、流程或骨架，就像是上例中的step1到step3一樣，至於流程中的step1到step3如何實作並不規定，而留給實作的人自行決定，這就是Template Method模式的目的。<br><br>抽象類別與介面的差別之一，也正在於抽象類別可以先實作其中一些方法，而介面則是完全僅規定接口，使用Template Method模式就可以看出兩者之間在應用上的一個差別。<br><br>僅以step1到step3這樣的操作來看Template Method模式，似乎彰顯示不出其實作骨架，而將實作部份留待子類的實用性，在 Gof 書中所舉的例子是與 Factory Method 模式結合的一個例子；通常開啟一個檔案的流程是相似的，例如文字檔或二進位檔，不外乎檢查檔案是否可開啟、讀取檔案、設定顯示等流程，可以使用 Template Method模式來規範這個流程：&nbsp;<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public abstract class Application { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // ..... </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void openDocument(String name) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Template Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!canOpenDocument(name)) { // unable to open file </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // show error message, throw exception </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Document doc = createDocument(); // Factory Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(doc != null) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _docs.addDocument(doc); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Template Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aboutToOpenDocument(doc); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doc.open(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; doc.doRead(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // Factory Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public abstract Document createDocument(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // Template Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public abstract boolean canOpenDocument(String name); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public abstract void aboutToOpenDocument(Document doc); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;} </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;public class MyApplication extends Application { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // implement Factory Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void Document createDocument() { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new MyDocument(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // implement Template Method </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void boolean canOpenDocument(String name) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // implemented code here </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; public void aboutToOpenDocument(Document doc) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // implemented code here </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;}</span><br></div>
&nbsp;<br><a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/FactoryMethod.htm">Factyro Method模式</a>將實際要創建的物件推遲至子類中決定，而 Template Method模式則是將流程框架的實作留待子類來解決，事實上在這個例子中，您也可以將createDocument()看作是Template Method模式中的一個方法，從物件創建的角度來看它是Factory Method，而從流程框架的角度來看，它則是Template Method模式的一個方法實作。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111192.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:52 <a href="http://www.blogjava.net/jesson2005/articles/111192.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Memento 模式</title><link>http://www.blogjava.net/jesson2005/articles/111187.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111187.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111187.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111187.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111187.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111187.html</trackback:ping><description><![CDATA[您希望您的程式中具有復原機制，如果您直接在物件中建立復原機制，這會使得物件本身的職責加重，並且使得物件的重用性降低。<br><br>與其在物件內建立復原機制，不如將復原機制從物件中脫離出來，這個時候您可以使用Memento模式來達成這個功能。<br><br>Memento模式在Originator中保留一個Memento成員，這個Memento可以包括Originator的成員資訊，在外部的話， Memento可以由一個Caretaker維護，每對Originator作一個動作，Caretaker就保留Originator動作前的成員狀態，如果以後要復原物件狀態，只要從Caretaker取回Memento物件，對Originator進行狀態復原。<br><br>Memento模式的 UML 類別結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=Memento style="WIDTH: 526px; HEIGHT: 150px" alt=Memento src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/memento-1.jpg"><br><br>
<div style="TEXT-ALIGN: left">圖中的Caretaker是用來保留原發者所創造的備忘錄物件，以供日後復原時取回，state表示一個內部狀態，內部狀態多時，也可以將之組織為一個類別，Caretaker維護的Memento可以是多個，用來實現Redo與Undo多次的功能。<br><br>下面提供一個簡單的實作，看看如何實現Memento模式：<br>
<ul>
    <li>Originator.java </li>
</ul>
<pre>public class Originator { <br>    private String name; <br>    private String phone;<br><br>    public Originator(String name, String phone) { <br>        this.name = name; <br>        this.phone = phone; <br>    }<br><br>    // Some operations make state changed <br>    public void someOperation() { <br>        name = "noboby"; <br>        phone = "911-911"; <br>    } <br><br>    // recover object's state <br>    public void setMemento(Memento m) { <br>        this.name = m.getName(); <br>        this.phone = m.getPhone(); <br>    }<br><br>    public Memento createMemento() { <br>        return new Memento(name, phone); <br>    }<br><br>    public void showInfo() { <br>        System.out.println("Name: " + name + <br>                         "\nPhone: " + phone + "\n"); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>Memento.java </li>
</ul>
<pre>public class Memento { <br>    private String name; <br>    private String phone;<br><br>    public Memento(String name, String phone) { <br>        this.name = name; <br>        this.phone = phone; <br>    }<br><br>    public String getName() { <br>        return name; <br>    }<br><br>    public String getPhone() { <br>        return phone; <br>    }<br><br>    public void setName(String name) { <br>        this.name = name; <br>    }<br><br>    public void setPhone(String phone) { <br>        this.phone = phone; <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Caretaker.java </li>
</ul>
<pre>public class Caretaker { <br>    private Memento memento;<br><br>    public void setMemento(Memento memento) { <br>        this.memento = memento; <br>    }<br><br>    public Memento getMemento() { <br>        return memento; <br>    }<br>} <br></pre>
\
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        Originator originator = <br>                      new Originator("Justin", "888-8888"); <br>        Caretaker caretaker = new Caretaker(); <br><br>        // save object's memento <br>        caretaker.setMemento(originator.createMemento()); <br><br>        originator.showInfo(); <br>        // some operations make the object's state changed <br>        originator.someOperation(); <br>        originator.showInfo(); <br><br>        // use memento to recover object's state <br>        originator.setMemento(caretaker.getMemento()); <br>        originator.showInfo(); <br>    } <br>} <br></pre>
<br>可以結合 <a class=wikilink href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/CommandPattern.htm">Command 模式</a> 來實作Redo/Undo的功能，將操作前後的物件狀態記錄下來，並記錄所使用的命令，當要實現Undo/Redo時，只要取回Memento物件以復原物件狀態即可。<br></div>
</div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:50 <a href="http://www.blogjava.net/jesson2005/articles/111187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Observer 模式</title><link>http://www.blogjava.net/jesson2005/articles/111189.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111189.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111189.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111189.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111189.html</trackback:ping><description><![CDATA[假設今天您設計一個試算表程式，當中有一個資料物件，您可以用表格圖形物件、柱狀圖形物件、圓餅圖形物件等方式來呈現物件，無論您是用哪種圖形物件，重點是若資料物件的內容作了更改，則圖形物件的內容也必須跟著修改，或許您的程式中有兩個以上的圖形物件來呈現資料，您在圖形物件上更動資料，則另一個圖形物件也必須作出相對應的變化。
<table width="50%" align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top align=middle><small><strong>主題</strong> </small></td>
            <td vAlign=top align=middle colSpan=3><small>資料物件 </small></td>
        </tr>
        <tr>
            <td vAlign=top align=middle><small><strong>觀察者</strong> </small></td>
            <td vAlign=top align=middle><small>柱狀圖形 </small></td>
            <td vAlign=top align=middle><small>表格圖形 </small></td>
            <td vAlign=top align=middle><small>圓餅圖形 </small></td>
        </tr>
    </tbody>
</table>
<br clear=all><br>又假設您今天設計一個網路遊戲，您在伺服器上維護一個連線客戶端共享的資料物件，當其中一個客戶端作了操作，將對此資料物件作修改，則伺服器必須通知其它客戶端作相對應的變化（像是人物位置走動、建了一個城堡等）。
<table width="50%" align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top align=middle><small><strong>主題</strong> </small></td>
            <td vAlign=top align=middle colSpan=3><small>資料物件 </small></td>
        </tr>
        <tr>
            <td vAlign=top align=middle><small><strong>觀察者</strong> </small></td>
            <td vAlign=top align=middle><small>客戶端一 </small></td>
            <td vAlign=top align=right><small>客戶端二</small></td>
            <td vAlign=top align=middle><small>客戶端三 </small></td>
        </tr>
    </tbody>
</table>
<br clear=all><br>在Observer模式中的主角為主題（subject）與觀察者（observer），觀察者訂閱它感興趣的主題，一個主題可以被多個觀察者訂閱，當主題的狀態發生變化時，它必須通知（notify）所有訂閱它的觀察者，觀察者檢視主題的狀態變化，並作出對應的動作，所以Observer 模式也稱之為Publish-Subscribe模式。 <br><br>Observer模式的 UML 圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=Observer style="WIDTH: 544px; HEIGHT: 268px" alt=Observer src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/observer-1.jpg"></div>
<br>Subject類中有一個notify()方法，通常是在Subject的狀態發生改變時呼叫它，notify()中會呼叫 Observer的update()方法，通常會先取得Subject的新狀態，然後更新Observer的顯示或行為，這個過程我們可以透過 Sequence Diagram來表達： <br>
<div style="TEXT-ALIGN: center"><img title=Observer style="WIDTH: 378px; HEIGHT: 294px" alt=Observer src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/observer-2.jpg"><br></div>
<br>在Java中支援觀察者模式，要成為觀察者的類必須實作Observer介面，這個介面中定義了一個update()方法，這個方法會被主題物件在通知狀態變化時呼叫，您必須在這個方法中實作您所想要的對應行為。<br><br>主題物件會是Observable的子類，在這邊注意兩個重要的方法：setChanged()與notifyObserver()。 setChanged()是用來設定主題物件的狀態已經被改變，而notifyObserver()方法會通知所要訂閱主題物件的觀察者，調用其 update()方法。<br><br>有興趣的話，建議看一下Java的Observable.java中是如何實作的，這有助於瞭解Observer模式的運作方式。
<img src ="http://www.blogjava.net/jesson2005/aggbug/111189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:50 <a href="http://www.blogjava.net/jesson2005/articles/111189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: State 模式</title><link>http://www.blogjava.net/jesson2005/articles/111190.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111190.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111190.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111190.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111190.html</trackback:ping><description><![CDATA[如果您不瞭解TCP的連線方式，在看 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm"><span class=wikilink>Gof</span> 的書</a>介紹State模式時，大概會看得一頭霧水吧！TCP的連線狀態圖，光是要瞭解就要花點精神了，它的連線狀態很多，用來說明狀態模式確實很適合，但不適合教導初學模式的人。 <br><br>由簡單的開始會比較好理解狀態模式的作用，先來看一個例子，如果您有一個只能順時針轉動的瓦斯開關，轉動一次的狀態為off、 small fire、medium fire與large fire，您如何在程式中控制狀態的變化與行為呢？一個最簡單的方式就是用if..else或是switch流程來控制，例如：<br>
<ul>
    <li>State.java </li>
</ul>
<pre>public class State { <br>    private int state;<br><br>    public State() { <br>        state = 0; <br>    } <br><br>    public void switchFire() { <br>        if (state == 0) { <br>            state = 1; <br>            System.out.println( "small fire" ); <br>        } else if (state == 1) { <br>            state = 2; <br>            System.out.println( "medium fire" ); <br>        } else if (state == 2) { <br>            state = 3; <br>            System.out.println( "large fire" ); <br>        } else { <br>            state = 0; <br>            System.out.println( "turning off" ); <br>        } <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>     public static void main(String[] args) { <br>        State state = new State();<br><br>        state.switchFire(); <br>        state.switchFire(); <br>        state.switchFire(); <br>        state.switchFire(); <br>    } <br>} <br></pre>
<br>這個方法很簡單，每個人都會，但如果您的狀態變化並不是流水式的變化，而是像TCP連線狀態一樣，會是一個網絡圖的時候，用 if...else或switch來寫的話，您的程式就會亂的不像話了；來考慮如何讓物件控制自己的狀態轉換與所應表現的行為，這個程式可以這樣改寫：<br>
<ul>
    <li>IState.java </li>
</ul>
<pre>public interface IState { <br>    public void switchFire(FireSwitch sw); <br>} <br></pre>
<br>
<ul>
    <li>OffState </li>
</ul>
<pre>public class OffState implements IState { <br>    public void switchFire(FireSwitch sw) { <br>        sw.setState(new SmallState()); <br>        System.out.println( "small fire" ); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>SmallState.java </li>
</ul>
<pre>public class SmallState implements IState { <br>    public void switchFire(FireSwitch sw) { <br>        sw.setState(new MediumState()); <br>        System.out.println( "medium fire" ); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>MediumState.java </li>
</ul>
<pre>public class MediumState implements IState { <br>    public void switchFire(FireSwitch sw) { <br>        sw.setState(new LargeState()); <br>        System.out.println( "large fire" ); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>LargeState.java </li>
</ul>
<pre>public class LargeState implements IState { <br>    public void switchFire(FireSwitch sw) { <br>        sw.setState(new OffState()); <br>        System.out.println( "off fire" ); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>FireSwitch.java </li>
</ul>
<pre>public class FireSwitch { <br>    private State current;<br><br>    public FireSwitch() { <br>        current = new OffState(); <br>    }<br><br>    public void setState(State s) { <br>        current = s; <br>    }<br><br>    public void switchFire() { <br>        current.switchFire(this); <br>    }<br>} <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        FireSwitch fireSwitch = new FireSwitch();<br>        fireSwitch.switchFire(); <br>        fireSwitch.switchFire(); <br>        fireSwitch.switchFire(); <br>        fireSwitch.switchFire(); <br>    } <br>} <br></pre>
<br>程式執行結果與上一個例子是一樣的，但這次並沒有用流程控制來進行狀態轉換，而由物件自行控制自己的狀態，與必須表現的行為，這個方式就是State 模式，將這個例子的 UML 類別結構畫出就如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=State style="WIDTH: 454px; HEIGHT: 211px" alt=State src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/state-1.jpg"><br></div>
<br>再進一步考慮開關可以順時針與逆時針轉動，這時如果您仍以if...else或switch來寫，就會讓流程顯示複雜，來看看如何使用狀態模式來撰寫： <br>
<ul>
    <li>IState.java </li>
</ul>
<pre>public interface IState { <br>    public void switchClockWise(FireSwitch sw); <br>    public void switchCountClock(FireSwitch sw); <br>}  <br></pre>
<br>
<ul>
    <li>OffState.java </li>
</ul>
<pre>public class OffState implements IState { <br>    public void switchClockWise(FireSwitch sw) { <br>        sw.setState(new SmallState()); <br>        System.out.println("small fire"); <br>    }<br><br>    public void switchCountClock(FireSwitch sw) { <br>        sw.setState(new LargeState()); <br>        System.out.println("large fire"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>SmallState.java </li>
</ul>
<pre>public class SmallState implements IState { <br>    public void switchClockWise(FireSwitch sw) { <br>        sw.setState(new MediumState()); <br>        System.out.println("medium fire"); <br>    } <br><br>    public void switchCountClock(FireSwitch sw) { <br>        sw.setState(new OffState()); <br>        System.out.println("off fire"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>MediumState.java </li>
</ul>
<pre>public class MediumState implements IState { <br>    public void switchClockWise(FireSwitch sw) { <br>        sw.setState(new LargeState()); <br>        System.out.println("large fire"); <br>    }<br> <br>    public void switchCountClock(FireSwitch sw) { <br>        sw.setState(new SmallState()); <br>        System.out.println("small fire"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>LargeState.java </li>
</ul>
<pre>public class LargeState implements State { <br>    public void switchClockWise(FireSwitch sw) { <br>        sw.setState(new OffState()); <br>        System.out.println("off fire"); <br>    }<br><br>    public void switchCountClock(FireSwitch sw) { <br>        sw.setState(new MediumState()); <br>        System.out.println("medium fire"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>FireSwitch.java </li>
</ul>
<pre>public class FireSwitch { <br>    private State current;<br><br>    public FireSwitch() { <br>        current = new OffState(); <br>    }<br><br>    public void setState(State s) { <br>        current = s; <br>    }<br><br>    public void switchClockWise() { <br>        current.switchClockWise(this); <br>    }<br><br>    public void switchCountClock() { <br>       current.switchCountClock(this); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        FireSwitch fireSwitch = new FireSwitch();<br><br>        fireSwitch.switchClockWise(); <br>        fireSwitch.switchClockWise(); <br>        fireSwitch.switchClockWise(); <br>        fireSwitch.switchClockWise(); <br><br>        System.out.println();<br><br>        fireSwitch.switchCountClock(); <br>        fireSwitch.switchCountClock(); <br>        fireSwitch.switchCountClock(); <br>        fireSwitch.switchCountClock(); <br>    } <br>} <br></pre>
<br>接下來您可以任意的轉動開關了，無論是順時針轉動或是逆時針轉動，狀態的轉換都由物件自己來表現，這是雙向狀態轉換下的例子，如果一個狀態可能轉換至三個以上的狀態，使用State模式就更可以看出它的好處了，就像Gof的TCP連線例子一樣，如果您瞭解TCP連線，可以看看原書是如何實現TCP連線之間的狀態轉換的。 <br><br>State模式的UML結構圖如下： <br>
<div style="TEXT-ALIGN: center"><img title=State style="WIDTH: 383px; HEIGHT: 217px" alt=State src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/state-2.jpg"></div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:50 <a href="http://www.blogjava.net/jesson2005/articles/111190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Mediator 模式</title><link>http://www.blogjava.net/jesson2005/articles/111186.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111186.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111186.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111186.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111186.html</trackback:ping><description><![CDATA[Mediator的意思是中介者、調節者、傳遞物，顧名思義，這個模式在程式中必然負擔一個中介、調節、傳遞的工作。<br><br>物件導向設計中，每個物件所負擔的工作儘可能的簡單明瞭，鼓勵物件將工作分布至其它物件上，讓一群工作屬性相同的物件得以共同合作，即所謂高聚合性，以增加物件的可重用性。<br><br>然而在組織物件工作的同時，物件彼此之間可能知道彼此的存在，並相互依賴，這就使得物件之間的耦合性相對的提高，最差的情況下，所有的物件都知道彼此的存在，這又會使得系統的重用性降低。<br><br>Mediator模式用一個中介的物件來封裝物件彼此之間的交互，物件之間並不用互相知道另一方，這可以降低物件之間的耦合性，如果要改變物件之間的交互行為，也只需要對Mediator加以修改即可。<br><br>在 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm">Gof 的書</a> 中所舉的例子為對話方塊組件；例如，當一個特定的輸入欄為空時，另一個按鈕不能使用；在ListBox的選項中選擇一個項目，將會改變另一個欄位的內容；反過來的，輸入欄位的內容也會影響ListBox的選擇等等。 <br>
<div style="TEXT-ALIGN: center"><img title=Mediator style="WIDTH: 478px; HEIGHT: 374px" alt=Mediator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/mediator-1.jpg"><br></div>
<br>在這個例子中，可以設計對話方塊中的組件知道彼此的存在，由一個直接影響另一個（或多個）組件，但最好的方法，還是設計一個Mediator，由它來協調組件之間的交互，例如設計一個FontDialogDirector類別來作為中介者。 <br>
<div style="TEXT-ALIGN: center"><img title=Mediator style="WIDTH: 395px; HEIGHT: 235px" alt=Mediator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/mediator-2.jpg"><br></div>
<br>可以從Sequence Diagram來瞭解Mediator的運作：<br>
<div style="TEXT-ALIGN: center"><img title=Mediator style="WIDTH: 480px; HEIGHT: 541px" alt=Mediator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/mediator-3.jpg"><br></div>
<br>當ListBox發生變化時，它會呼叫Mediator的listBoxChanged()方法，Mediator取得變化的組件之狀態，並重新設定所有與它有交互的組件，同樣的，其它的組件發生變化時，也呼叫Mediator上對應的方法，由Mediator來取得組件變化，並設定其它互動的組件。<br><br>簡單的說，Mediator設計有與組件溝通的介面，介面中封裝了與其它組件互動細節，組件與組件之間不用知道彼此的存在，它們只要與Mediator溝通就好了，利用這種方式，可以切開組件與組件之間的耦合。<br><br>Mediator模式的 UML 結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=Mediator style="WIDTH: 482px; HEIGHT: 191px" alt=Mediator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/mediator-4.jpg"><br></div>
基本上Mediator模式在使用的彈性很大，由Sequence Diagram理解概念，會比從Class Diagram瞭解結構來得重要，不過在Class Diagram中可以注意的是類別的名稱，Colleague是同事的意思，將一群共事的元件視為一群共同合作的同事，為了使同事之間的活動獨立，並使得團隊合作的交互更具彈性，需要一個Mediator來協調同事之間的業務行為。
<img src ="http://www.blogjava.net/jesson2005/aggbug/111186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:48 <a href="http://www.blogjava.net/jesson2005/articles/111186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Interpreter 模式</title><link>http://www.blogjava.net/jesson2005/articles/111184.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:47:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111184.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111184.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111184.html</trackback:ping><description><![CDATA[對於一個具有層次節點關係的問題來說，如果您要剖析每一個節點，您可以使用Interpreter模式，直譯器模式有些類似演算法中的個別擊破方式，對每一個父節點我們剖析出其子節點組合，然而交給子節點剖析物件繼續剖析，直到剖析至終端節點為止。<br><br>舉個例子來說明好了，先說明的是，這個例子是改寫自 <a href="http://www.drmaster.com.tw/info.asp?NO=PG20214">Design Patterns於Java語言之實習應用</a> 第23章的範例，我將之更簡化了，以讓大家將焦點能集中在如何使用Interpreter模式，以及如何實用。<br><br>假設您要實作一個Interpreter，這個Interpreter可以直譯您文字檔中的程式，並依您自訂的程式文法來執行程式，幾個簡單的程式如下：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">PROGRAM </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; PRINT dog SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; PRINT is SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; PRINT an SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; PRINT animai </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">END </span><br></div>
&nbsp;<br>您的這式程個會印出"dog is an animal"的文字，再來一個例子是：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">PROGRAM </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; REPEAT 2 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LINEBREAK </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT dog </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BREAK </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; END </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">END</span><br></div>
&nbsp;<br><br>這個程式要印出：<br>
<table style="WIDTH: 963px; COLOR: rgb(255,255,255); FONT-FAMILY: Times New Roman,Times,serif; HEIGHT: 32px; BACKGROUND-COLOR: rgb(0,0,0); TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=0>
    <tbody>
        <tr>
            <td><small>------------------------------ <br style="FONT-WEIGHT: bold">&nbsp;dog <br style="FONT-WEIGHT: bold">------------------------------ <br style="FONT-WEIGHT: bold">&nbsp;dog</small></td>
        </tr>
    </tbody>
</table>
<span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><br>您也可以任意的組合程式，例如：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">PROGRAM </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; PRINT begin </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; BREAK </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; REPEAT 3 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; REPEAT 2 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT dog SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT is SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT a SPACE </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT animal </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BREAK </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; END </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">END</span><br></div>
&nbsp;<br><br>這個程式中的幾個關鍵字是PROGRAM、PRINT、SPACE、BREAK、LINEBREAK、REPEAT、END， PROGRAM是表示程式開始，以END作結，PRINT可以印出一個無空白的字串，SPACE印出一個空白，BREAK是換行，而LINEBREAK是畫一個直線並換行，REPEAT是迴圈指令，可以指定迴圈次數，以END作結。<br><br>觀察程式，可以制定出以下的文法，如下：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&lt;program&gt; ::= PROGRAM &lt;command list&gt; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&lt;command list&gt; ::= &lt;command&gt;* END </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&lt;command&gt; ::= &lt;repeat command&gt; | &lt;primitive command&gt; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&lt;repeat command&gt; ::= REPEAT &lt;number&gt; &lt;command list&gt; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&lt;primitive command&gt; ::= PRINT &lt;string&gt; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | BREAK | SPACE | LINEBREAK</span><br></div>
&nbsp;<br><br>程式文法制定需要對程式進行語句分析與定義，在這邊並不討論這個課題，在程式中，command節點由primitive或repeat兩個節點任意組合，一個command list節點則是零個以上的command節點組合而成，其中repeat還可以組合command list節點，這是組合模式的應用，可以在程式中組合巢狀迴圈。<br><br>在直譯程式時，以讀到PROGRAM作為開始節點，接下來我們剖析程式為command list 節點，並將它們丟給專門剖析command list的物件繼續剖析，這個物件將之分析，看是不是有repeat command或primitive command節點，如果有就再往下交由專屬物件進行剖析，如此層層剝開，並由專屬物件負責剖析工作。<br><br>Interpreter模式的基本觀念就如上所示，先來看看如何以程式實現剖析的過程，下面這個程式會剖析您的程式，並將程式加上對應的括號來將同一個區塊組合起來，以表示它完成剖析之後的結果：<br>
<ul>
    <li>INode.java </li>
</ul>
<pre>public interface INode { <br>    public void parse(Context context); <br>} <br></pre>
<br>
<ul>
    <li>ProgramNode.java </li>
</ul>
<pre>// &lt;program&gt; ::= PROGRAM &lt;command list&gt; <br>public class ProgramNode implements INode { <br>    private INode commandListNode; <br>    public void parse(Context context) { <br>        context.skipToken("PROGRAM"); <br>        commandListNode = new CommandListNode(); <br>        commandListNode.parse(context); <br>    } <br><br>    public String toString() { <br>        return "[PROGRAM " + commandListNode + "]"; <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>CommandListNode.java </li>
</ul>
<pre>import java.util.Vector; <br><br>// &lt;command list&gt; ::= &lt;command&gt;* END <br>public class CommandListNode implements INode { <br>    private Vector list = new Vector();<br><br>    public void parse(Context context) { <br>        while (true) { <br>            if (context.currentToken() == null) { <br>                System.err.println("Missing 'END'"); <br>                 break; <br>            } else if (<br>                    context.currentToken().equals("END")) { <br>                context.skipToken("END"); <br>                break; <br>            } else { <br>                INode commandNode = new CommandNode(); <br>                commandNode.parse(context); <br>                list.add(commandNode); <br>            } <br>        } <br>    }<br><br>    public String toString() { <br>        return "" + list; <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>CommandNode.java </li>
</ul>
<pre>// &lt;command&gt; ::= &lt;repeat command&gt; | &lt;primitive command&gt; <br>public class CommandNode implements INode { <br>    private INode node;<br><br>    public void parse(Context context) { <br>        if (context.currentToken().equals("REPEAT")) { <br>            node = new RepeatCommandNode(); <br>            node.parse(context); <br>        } else { <br>            node = new PrimitiveCommandNode(); <br>            node.parse(context); <br>        } <br>    }<br><br>    public String toString() { <br>        return node.toString(); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>RepeatCommandNode.java </li>
</ul>
<pre>public class RepeatCommandNode implements INode { <br>    private int number; <br>    private INode commandListNode; <br><br>    public void parse(Context context) { <br>        context.skipToken("REPEAT"); <br>        number = context.currentNumber(); <br>        context.nextToken(); <br>        commandListNode = new CommandListNode(); <br>        commandListNode.parse(context); <br>    }<br><br>    public String toString() {<br>        return "[REPEAT " + number + " " <br>                  + commandListNode + "]"; <br>    } <br>}<br></pre>
<br>
<ul>
    <li>PrimitiveCommandNode.java </li>
</ul>
<pre>// &lt;primitive command&gt; ::= PRINT &lt;string&gt; <br>//                           | SPACE | BREAK | LINEBREAK <br>public class PrimitiveCommandNode implements INode {<br>    private String name;<br>    private String text;<br><br>    public void parse(Context context) { <br>        name = context.currentToken(); <br>        context.skipToken(name); <br>        if (!name.equals("PRINT") &amp;&amp; !name.equals("BREAK") <br>             &amp;&amp; !name.equals("LINEBREAK") <br>             &amp;&amp; !name.equals("SPACE")) { <br>            System.err.println("Undefined Command"); <br>        }<br><br>        if (name.equals("PRINT")) { <br>            text = context.currentToken(); <br>            name += text; <br>            context.nextToken(); <br>        } <br>    }<br><br>    public String toString() { <br>        return name; <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Context.java </li>
</ul>
<pre>import java.util.*; <br><br>public class Context { <br>    private StringTokenizer tokenizer; <br>    private String currentToken; <br><br>    public Context(String text) { <br>        tokenizer = new StringTokenizer(text); <br>        nextToken(); <br>    } <br><br>    public String nextToken() { <br>        if (tokenizer.hasMoreTokens()) { <br>            currentToken = tokenizer.nextToken(); <br>        } else { <br>            currentToken = null; <br>        } <br>        return currentToken; <br>    } <br><br>    public String currentToken() { <br>        return currentToken; <br>    } <br><br>    public void skipToken(String token) { <br>        if (!token.equals(currentToken)) { <br>            System.err.println("Warning: " + token + <br>                          " is expected, but " + <br>                          currentToken + " is found."); <br>        } <br>        nextToken(); <br>    } <br><br>    public int currentNumber() { <br>        int number = 0; <br>        try { <br>            number = Integer.parseInt(currentToken); <br>        } catch (NumberFormatException e) { <br>            System.err.println("Warning: " + e); <br>        } <br>        return number; <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>import java.util.*; <br>import java.io.*;<br><br>public class Main { <br>    public static void main(String[] args) { <br>        try { <br>            BufferedReader reader = new <br>                  BufferedReader(new FileReader(args[0])); <br>            String text; <br>            while ((text = reader.readLine()) != null) { <br>                System.out.println("text = \"" + <br>                                       text + "\""); <br>                INode node = new ProgramNode(); <br>                node.parse(new Context(text)); <br>                System.out.println("node = " + node); <br>            } <br>        } <br>        catch (ArrayIndexOutOfBoundsException e) { <br>            System.err.println(<br>                   "Usage: java Main yourprogram.txt"); <br>        } <br>        catch (Exception e) { <br>            e.printStackTrace(); <br>        } <br>    } <br>} </pre>
<br>假設您的程式是這樣寫的：<br>
<ul>
    <li>program.txt </li>
</ul>
<pre>PROGRAM PRINT xxx END<br>PROGRAM REPEAT 4 PRINT xxx END END <br>PROGRAM REPEAT 4 PRINT xxx PRINT "yyy" END END</pre>
<br>則執行Intrepreter程式之後會是：<br>
<table style="WIDTH: 963px; COLOR: rgb(255,255,255); FONT-FAMILY: Times New Roman,Times,serif; HEIGHT: 32px; BACKGROUND-COLOR: rgb(0,0,0); TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=0>
    <tbody>
        <tr>
            <td><small>&nbsp;$ java Main program.txt <br>&nbsp;text = "PROGRAM PRINT xxx END" <br>&nbsp;node = [PROGRAM [PRINTxxx]] <br><br>&nbsp;text = "PROGRAM REPEAT 4 PRINT xxx END END" <br>&nbsp;node = [PROGRAM [[REPEAT 4 [PRINTxxx]]]] <br><br>&nbsp;text = "PROGRAM REPEAT 4 PRINT xxx PRINT "yyy" END END" <br>&nbsp;node = [PROGRAM [[REPEAT 4 [PRINTxxx, PRINT"yyy"]]]]</small></td>
        </tr>
    </tbody>
</table>
<span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><br>這個範例程式基本上已經顯示了直譯器模式的工作原理，如何讓程式直譯之後能夠工作，這待會再示範，先來看一下Intrepreter模式的 UML&nbsp;類別結構圖： <br>
<div style="TEXT-ALIGN: center"><img title=Intrepreter style="WIDTH: 444px; HEIGHT: 204px" alt=Intrepreter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/interpreter-1.jpg"><br></div>
<br>TerminalExpression就像我們的primitive command，再剖析下去已經沒有子節點了，而NonterminalExpression就像是repeat command，注意到其中也使用了組合模式，就如之前所說的，組合模式讓可以遞迴的組合句子為更複雜的語句。<br><br>您已經會剖析句子了，接下來要如何讓這個直譯器真正工作，雖然程式中使用toString()來表示每一個節點的剖析結果，但事實上，這個程式也已經說明了如何讓剖析的結果真正運作了，既然已經記錄好剖析之後的語句順序了，只要由上而下追蹤剖析結果，就一定可以執行到 primitive command，且順序符合自訂的程式原始碼的需求，這只要將toString()改為execute()，並作一些轉發與重複執行的修改就可以了，直接來看程式會比較容易理解：<br>
<ul>
    <li><span class=createlink>INode</span>.java </li>
</ul>
<pre>public interface INode {<br>    public void parse(Context context);<br>    public void execute();<br>} <br></pre>
<br>
<ul>
    <li>ProgramNode.java </li>
</ul>
<pre>// &lt;program&gt; ::= PROGRAM &lt;command list&gt;<br>public class ProgramNode implements INode {<br>    private INode commandListNode;<br><br>    public void parse(Context context) {<br>        context.skipToken("PROGRAM");<br>        commandListNode = new CommandListNode();<br>        commandListNode.parse(context);<br>    }<br><br>    public void execute() {<br>        commandListNode.execute();<br>    }<br><br>    public String toString() {<br>        return "[PROGRAM " + commandListNode + "]";<br>    }<br>} <br></pre>
<br>
<ul>
    <li>CommandListNode.java </li>
</ul>
<pre>import java.util.*;    <br><br>// &lt;command list&gt; ::= &lt;command&gt;* END<br>public class CommandListNode implements INode {<br>    private Vector list = new Vector();<br>    private INode commandNode;<br><br>    public void parse(Context context) {<br>        while (true) {<br>            if (context.currentToken() == null) {<br>                System.err.println("Missing 'END'");<br>                break;<br>            } else if(context.currentToken().equals("END")) {<br>                context.skipToken("END");<br>                break;<br>            } else {<br>                commandNode = new CommandNode();<br>                commandNode.parse(context);<br>                list.add(commandNode);<br>            }<br>        }<br>    }<br><br>    public void execute() {<br>        Iterator it = list.iterator();<br>        while (it.hasNext()) {<br>            ((CommandNode)it.next()).execute();<br>        }<br>    }<br><br>    public String toString() {<br>        return "" + list;<br>   }<br>} <br></pre>
<br>
<ul>
    <li>CommandNode.java </li>
</ul>
<pre>// &lt;command&gt; ::= &lt;repeat command&gt; | &lt;primitive command&gt;<br>public class CommandNode implements INode {<br>    private INode node;<br><br>    public void parse(Context context) {<br>        if (context.currentToken().equals("REPEAT")) {<br>            node = new RepeatCommandNode();<br>            node.parse(context);<br>        } else {<br>            node = new PrimitiveCommandNode();<br>            node.parse(context);<br>        }<br>    }<br><br>    public void execute() {<br>        node.execute();<br>    }<br><br>    public String toString() {<br>        return node.toString();<br>    }<br>} <br></pre>
<br>
<ul>
    <li>PrimitiveCommandNode.java </li>
</ul>
<pre>// &lt;primitive command&gt; ::= PRINT &lt;string&gt; <br>//                           | SPACE | BREAK | LINEBREAK<br>public class PrimitiveCommandNode implements INode {<br>    private String name;<br>    private String text;<br><br>    public void parse(Context context) {<br>        name = context.currentToken();<br>        context.skipToken(name);<br>        if (!name.equals("PRINT") &amp;&amp; !name.equals("BREAK") <br>                           &amp;&amp; !name.equals("LINEBREAK") <br>                           &amp;&amp; !name.equals("SPACE")) {<br>            System.err.println("Undefined Command");<br>        }<br><br>        if (name.equals("PRINT")) {<br>            text = context.currentToken();<br>            context.nextToken();<br>        }<br>    } <br><br>    public void execute() {<br>        if(name.equals("PRINT"))<br>            System.out.print(text);<br>        else if(name.equals("SPACE"))<br>            System.out.print(" ");<br>        else if(name.equals("BREAK"))<br>            System.out.println();<br>        else if(name.equals("LINEBREAK"))<br>            System.out.println(<br>                  "\n------------------------------");<br>    }<br><br>    public String toString() {<br>        return name;<br>    }<br>} <br></pre>
<br>
<ul>
    <li>RepeatCommandNode.java </li>
</ul>
<pre>public class RepeatCommandNode implements INode {<br>    private int number;<br>    private INode commandListNode;<br><br>    public void parse(Context context) {<br>        context.skipToken("REPEAT");<br>        number = context.currentNumber();<br>        context.nextToken();<br>        commandListNode = new CommandListNode();<br>        commandListNode.parse(context);<br>    }<br><br>    public void execute() {<br>        for(int i = 0; i &lt; number; i++)<br>            commandListNode.execute();<br>    }<br><br>    public String toString() {<br>        return "[REPEAT " + number + " " + <br>                             commandListNode + "]";<br>    }<br>} <br></pre>
<br>
<ul>
    <li>Context.java </li>
</ul>
<pre>import java.util.*;<br><br>public class Context {<br>    private StringTokenizer tokenizer;<br>    private String currentToken;<br><br>    public Context(String text) {<br>        tokenizer = new StringTokenizer(text);<br>        nextToken();<br>    }<br><br>    public String nextToken() {<br>        if (tokenizer.hasMoreTokens()) {<br>            currentToken = tokenizer.nextToken();<br>        } else {<br>            currentToken = null;<br>        }<br>        return currentToken;<br>    }<br><br>    public String currentToken() {<br>        return currentToken;<br>    }<br><br>    public void skipToken(String token) {<br>        if (!token.equals(currentToken)) {<br>            System.err.println("Warning: " + token + <br>                           " is expected, but " + <br>                           currentToken + " is found.");<br>        }<br>        nextToken();<br>    }<br><br>    public int currentNumber() {<br>        int number = 0;<br>        try {<br>            number = Integer.parseInt(currentToken);<br>        } catch (NumberFormatException e) {<br>            System.err.println("Warning: " + e);<br>        }<br>        return number;<br>    }<br>} <br></pre>
<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>import java.util.*;<br>import java.io.*;<br><br>public class Main {<br>    public static void main(String[] args) {<br>        try {<br>            BufferedReader reader = new BufferedReader(<br>                               new FileReader(args[0]));<br>            String text;<br>            while ((text = reader.readLine()) != null) {<br>                System.out.println("text = \"" + text <br>                                      + "\"");<br>                INode node = new ProgramNode();<br>                node.parse(new Context(text));<br>                node.execute();<br>            }<br>        }<br>        catch (ArrayIndexOutOfBoundsException e) {<br>            System.err.println(<br>                 "Useage: java Main yourprogram.txt");<br>        }<br>        catch (Exception e) {<br>            e.printStackTrace();<br>        }<br>    }<br>}</pre>
<br>假設您的直譯程式稿是這麼撰寫的： <br>
<ul>
    <li>program.txt </li>
</ul>
<pre>PROGRAM REPEAT 4 LINEBREAK PRINT justin SPACE PRINT momor LINEBREAK END END</pre>
<br>則程式執行的結果就是： <br>
<table style="WIDTH: 963px; COLOR: rgb(255,255,255); FONT-FAMILY: Times New Roman,Times,serif; HEIGHT: 32px; BACKGROUND-COLOR: rgb(0,0,0); TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=0>
    <tbody>
        <tr>
            <td><small>&nbsp; $ java Main program.txt <br>&nbsp;text = "PROGRAM REPEAT 4 LINEBREAK PRINT justin SPACE <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRINT momor LINEBREAK END END" <br>&nbsp;------------------------------ <br>&nbsp;justin momor <br>&nbsp;------------------------------ <br><br>&nbsp;------------------------------ <br>&nbsp;justin momor <br>&nbsp;------------------------------ <br><br>&nbsp;------------------------------ <br>&nbsp;justin momor <br>&nbsp;------------------------------ <br><br>&nbsp;------------------------------ <br>&nbsp;justin momor <br>&nbsp;------------------------------&nbsp;</small></td>
        </tr>
    </tbody>
</table>
<span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><br><a href="http://www.drmaster.com.tw/info.asp?NO=PG20214">Design Patterns於Java語言之實習應用</a> 第23章的範例中，可以讓您依指令稿直譯，畫出任何的圖案，讓範例結合了工廠（Factory）模式、外觀（Facade）模式等等，在這邊建議您看看那個範例，看看不同的設計模式之間如何組合且相互合作。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:47 <a href="http://www.blogjava.net/jesson2005/articles/111184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Iterator 模式</title><link>http://www.blogjava.net/jesson2005/articles/111185.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:47:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111185.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111185.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111185.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111185.html</trackback:ping><description><![CDATA[在Java中提供有ArrayList類，您可以用它來設計一個動態的物件陣列，並在適當的時候取出陣列中的物件，假設今天您要循序的訪問ArrayList中的所有物件，則您可能採取這樣的方式： <br>
<ul>
    <li>Main.java </li>
</ul>
<pre>import java.util.*;<br><br>public class Main {<br>    public static void main(String[] args) {<br>        List arrayList = new ArrayList();<br><br>        for(int i = 0; i &lt; 10; i++) <br>            arrayList.add("Test " + i);<br><br>        for(int i = 0; i &lt; 10; i++) <br>            System.out.println(arrayList.get(i).toString()); <br>    } <br>} <br></pre>
<br>在這個例子中，很幸運的，您的ArrayList物件可以透過get()方法，使用索引加上迴圈的方式來循序訪問 ArrayList中的所有物件，不過並不是每一個聚合（aggregate）物件的內部實作都會是有索引結構的，也許是key/value的方式，或者是其它的方式。<br>由於每個聚合物件的內部實作方式不盡相同，如果您想要循序的訪問聚合物件中所有的物件，您要為不同的聚合物件設計不同的循序訪問介面，然而這並不是個好方法，結果是您設計的聚合物件上將會有很多的循序訪問方法，而使用您的聚合物件的人，還必須知道這個聚合物件的類型，才能進一步知道如何使用它所提供的方法。<br><br>與其在聚合物件上直接設計遍訪的介面，不如設計一個介面Iterator，上面設計有統一的循序訪問，當您想要循序訪問聚合物件中的物件時，將聚合物件中的物件加以包裝為一個Iterator後返回，客戶端只要面對Iterator所提供的介面，而不用面對為數眾多的聚合物件。<br><br>採取迭代器（Iterator）的方法，一個迭代器提供一個特定的遍訪方法，而使得設計人員無需關心聚合物件的類型（可能是ArrayList或 LinkedList等），例如上面這個程式可以改為：<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>import java.util.*;<br><br>public class Main { <br>    public static void main(String[] args) { <br>        List arrayList = new ArrayList();<br>        for(int i = 0; i &lt; 10; i++) <br>            arrayList.add("Test " +i);<br>        visit(arrayList.iterator()); <br>    }<br><br>    public static visit(Iterator iterator) {<br>        while(iterator.hasNext()) <br>            System.out.println(iterator.next().toString()); <br>    }<br>}</pre>
<br>如上所示的，iterator()方法會傳回一個Iterator物件，這個物件提供的循序訪問的統一介面，如果您查詢 Java API中的LinkedList所提供的方法，您會發現它的iterator()方法同樣也傳回Iterator物件，您無需關心您所使用的是 ArrayList或LinkedList，使用Iterator可以讓您以相同的方法來遍訪聚合物件的內容，以上例來說，visit()方法就可以重用，它不特地服務於ArrayList或LinkedList。<br>
<div style="TEXT-ALIGN: center"><img title=Iterator style="WIDTH: 419px; HEIGHT: 274px" alt=Iterator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/iterator-1.jpg"><br></div>
<br>意這個模型是簡化過後的版本，並不是Java中的設計，這麼作只是為了方便說明，這個圖形說明了Iterator模式的基本結構。再來看另一個在 Thinking in Java的例子，可以說明使用Iterator的好處：<br>
<ul>
    <li>HamsterMaze.java </li>
</ul>
<pre>import java.util.*; <br><br>class Hamster { <br>    private int hamsterNumber; <br>    Hamster(int i) { hamsterNumber = i; } <br>    public String toString() { <br>        return "This is Hamster #" + hamsterNumber; <br>    } <br>} <br><br>class Printer { <br>    static void printAll(Iterator e) { <br>        while(e.hasNext()) {<br>            System.out.println(e.next()); <br>        }<br>    } <br>} <br><br>public class HamsterMaze { <br>    public static void main(String[] args) { <br>        ArrayList v = new ArrayList(); <br>        for(int i = 0; i &lt; 3; i++) {<br>            v.add(new Hamster(i)); <br>        }<br>        Printer.printAll(v.iterator()); <br>    } <br>} <br></pre>
<br>對於Printer物件來說，它完全不用知道聚合物件的類型到底是ArrayList或是LinkedList，您只要傳回Iterator物件就可以了，剩下的就是使用Iterator物件所提供的循序訪問方法來訪問所有的物件。 <br><br>Iterator模式的 UML 結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=Iterator style="WIDTH: 345px; HEIGHT: 289px" alt=Iterator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/iterator-2.jpg"><br></div>
<br>使用Iterator模式，可以將循序訪問聚合對象的方法從該對象中分離出來，從而使得聚合對象的設計單純化，對客戶來說，他所要知道的是所使用的 Iterator而不是聚合對象的類型。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:47 <a href="http://www.blogjava.net/jesson2005/articles/111185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Command 模式</title><link>http://www.blogjava.net/jesson2005/articles/111182.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:46:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111182.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111182.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111182.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111182.html</trackback:ping><description><![CDATA[如果您寫過Java的Swing視窗程式，您可能使用過Command模式了，例如在您按下JMenuItem的「剪下」選項時，執行對JTextArea的選定文字之剪下動作，並將狀態列設定為文件已修改狀態。<br><br>在設計Swing時，設計人員是不可能知道使用Swing類別的人，在某個事件發生後所要執行的動作是什麼的，他們採用了Command模式，以上面的需求作為例子，一個實作的片段可能像是這個樣子：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">menuCut.addActionListener( </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; new ActionListener() { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed(ActionEvent e) { </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // textArea 是 JTextArea的一個實例 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textArea.cut();&nbsp;&nbsp; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; });</span><br></div>
&nbsp;<br>上面個這片段採用的是Java的匿名類別（Anonymous class），一個不具名的類別實作了ActionListener介面，它只有一個方法actionPerformed()，使用 addActionListener()為JMenuItem加入這個類別的實例，一但這個JMenuItem被按下，它就會調用 actionPerformed()方法，以執行您所定義的工作， UML 類別圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=Command style="WIDTH: 480px; HEIGHT: 269px" alt=Command src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/command-1.jpg"><br></div>
使用Command模式，您可以根據實際的需求來調用執行的物件，至於執行的細節封裝在事先定義好的方法（例如actionPerformed()方法，下面實際撰寫個程式作為示範：<br>
<ul>
    <li>Invoker.java </li>
</ul>
<pre>import java.util.*;<br><br>public class Invoker {<br>    private Map commands;<br>    <br>    public Invoker() {<br>        commands = new HashMap();<br>    }<br>    <br>    public void addCommand(String commName,<br>                           ICommand command) {<br>        commands.put(commName, command);<br>    }<br>    <br>    public void request(String commName) {<br>        ICommand command = (ICommand) commands.get(commName);<br>        command.execute();<br>    }<br>} <br></pre>
<br>
<ul>
    <li>ICommand.java </li>
</ul>
<pre>public interface ICommand {<br>    public void execute();<br>} <br></pre>
<br>
<ul>
    <li>UpperCaseHello.java </li>
</ul>
<pre>public class UpperCaseHello implements ICommand {<br>    private String name;<br>    <br>    public UpperCaseHello(String name) {<br>        this.name = name;    <br>    }<br>    <br>    public void execute() {<br>        System.out.println("HELLO, " + name.toUpperCase());<br>    }<br>} <br></pre>
<br>
<ul>
    <li>LowerCaseHello.java </li>
</ul>
<pre>public class LowerCaseHello implements ICommand {<br>    private String name;<br>    <br>    public LowerCaseHello(String name) {<br>        this.name = name;    <br>    }<br>    <br>    public void execute() {<br>        System.out.println("hello, " + name.toLowerCase());<br>    }<br>} <br></pre>
<br>Client模擬隨機的請求，Invoker事先並不知道Client會發出什麼需求，但它總是可以執行Client的請求：<br>
<ul>
    <li>Client.java </li>
</ul>
<pre>public class Client {<br>    public static void main(String[] args) {<br>        Invoker invoker = new Invoker();<br>        invoker.addCommand("JUSTIN", <br>                            new UpperCaseHello("Justin"));<br>        invoker.addCommand("momor", <br>                            new LowerCaseHello("momor"));<br>        <br>        // simulate random request<br>        String[] req = {"JUSTIN", "momor"};<br>        while(true) {<br>            int i = (int) (Math.random() * 10) % 2;<br>            invoker.request(req[i]);<br>        }<br>    }<br>} <br></pre>
<p>&#160;</p>
像上面這種將請求封裝起來的模式，就是Command模式，它將請求後的實作部份留待使用者實作，Command模式的UML類別圖如下所示：<br>
<div style="TEXT-ALIGN: center"><br></div>
<div style="TEXT-ALIGN: center"><img title=Command style="WIDTH: 408px; HEIGHT: 275px" alt=Command src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/command-2.jpg"><br><br></div>
Command模式是個相當常見的模式，除了先前談過的Swing視窗程式設計例子之外，現在許多<a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/MVCPattern.htm">Web MVC</a> <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/FrameWork.htm">Framework</a> 也都採用Command模式來設計Controller，在這樣的例子中，Container就好比Command模式中Client的角色，而前端 Controller（例如JSP技術中通常會採用的Dispatcher Servlet）就相當於Invoker，而Action則相當於ICommand的角色，至於Receiver角色就是您封裝在Action中執行的物件了。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:46 <a href="http://www.blogjava.net/jesson2005/articles/111182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Chain of Responsibility 模式</title><link>http://www.blogjava.net/jesson2005/articles/111181.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:45:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111181.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111181.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111181.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111181.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111181.html</trackback:ping><description><![CDATA[其實Chain of Responsibility的概念，即使是一個剛學程式設計的新手也會用到，一個簡單的 if...else if ... else 流程控制就有Chain of Responsibility的概念：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">if(/* 符合請求條件一 */) </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // 執行請求一 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">else if(/* 符合請求條件二 */) </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // 執行請求二 </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">else </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">&nbsp;&nbsp;&nbsp; // 執行預設請求或顯示訊息</span><br></div>
&nbsp;<br>這是從結構化程式設計的觀點來看Chain of Responsibility的概念，若使用物件的觀點來看Chain of Responsibility的話，有一個較佳的例子就是Java的例外處理機制，當程式中發生例外時，也比會catch所捕捉的例外是否符合，如果符合就執行所設定的處理，如果都沒有比對到適當的例外物件，就會將例外丟出try...catch區塊之外。<br><br>在 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm">Gof 的書</a> 中給定Chain of Responsibility目的為：使多個物件都有機會處理請求，以避免請求的發送者與接收者之間的耦合關係，將這些物件組合為一個鏈，並沿著這個鏈傳遞該請求，直到有物件處理它為止。<br><br>先用一個例子來說明使用if...else的方式來處理請求： <br>
<ul>
    <li>IHandler.java </li>
</ul>
<pre>public interface IHandler {<br>    public void handle(); <br>}  <br></pre>
<br>
<ul>
    <li>SymbolHandler.java </li>
</ul>
<pre>public class SymbolHandler implements IHandler { <br>    public void handle() { <br>       System.out.println("Symbol has been handled"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>CharacterHandler.java </li>
</ul>
<pre>public class CharacterHandler implements IHandler { <br>    public void handle() { <br>       System.out.println("Character has been handled"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>NumberHandler.java </li>
</ul>
<pre>public class NumberHandler implements IHandler { <br>    public void handle() { <br>       System.out.println("Number has been handled"); <br>    } <br>} <br></pre>
<br>
<ul>
    <li>Application.java </li>
</ul>
<pre>import java.io.*; <br><br>public class Application { <br>   public void run() throws Exception { <br>       System.out.print("Press any key then return: "); <br>       char c = (char) System.in.read(); <br><br>       IHandler handler = null; <br>       if (Character.isLetter(c)) {<br>         handler = new CharacterHandler(); <br>       }<br>       else if (Character.isDigit(c)) {<br>          handler = new NumberHandler(); <br>       }<br>       else {<br>          handler = new SymbolHandler(); <br>       }<br><br>       handler.handle(); <br>   } <br><br>   public static void main(String[] args) <br>                           throws IOException {<br>          Application app = new Application();<br>          app.run(); <br>   } <br>} <br></pre>
<br>這是一個很簡單的程式，可以判定您所輸入的是數字、字元或是符號，如果將之以物件的方式來組織物件之間的職責，可以將程式改寫如下：<br>
<ul>
    <li>Handler.java </li>
</ul>
<pre>public class Handler { <br>    private Handler successor;<br><br>    public void setSuccessor(Handler successor) { <br>        this.successor = successor; <br>    }<br><br>    public Handler getSuccessor() { <br>        return successor; <br>    }<br><br>    public void handleRequest(char c) { <br>        if(successor != null) <br>            successor.handleRequest(c); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>NumberHandler.java </li>
</ul>
<pre>public class NumberHandler extends Handler { <br>    public void handleRequest(char c) { <br>        if(Character.isDigit(c)) { <br>            System.out.println("Number has been handled"); <br>        } <br>        else {<br>            getSuccessor().handleRequest(c); <br>        }<br>    } <br>}  <br></pre>
<br>
<ul>
    <li>CharacterHandler.java </li>
</ul>
<pre>public class CharacterHandler extends Handler { <br>    public void handleRequest(char c) { <br>        if(Character.isLetter(c)) { <br>            System.out.println("Character has been handled"); <br>        } <br>        else {<br>            getSuccessor().handleRequest(c); <br>        }<br>    } <br>}  <br></pre>
<br>
<ul>
    <li>SymbolHandler.java </li>
</ul>
<pre>public class SymbolHandler extends Handler { <br>    public void handleRequest(char c) { <br>        System.out.println("Symbol has been handled"); <br>    } <br>}  <br></pre>
<br>
<ul>
    <li>Application.java </li>
</ul>
<pre>import java.io.*; <br><br>public class Application {<br>    public static void main(String[] args) <br>                                 throws IOException { <br>        Handler numberHandler = new NumberHandler(); <br>        Handler characterHandler = new CharacterHandler(); <br>        Handler symbolHandler = new SymbolHandler(); <br><br>        numberHandler.setSuccessor(characterHandler); <br>        characterHandler.setSuccessor(symbolHandler); <br><br>        System.out.print("Press any key then return: "); <br>        char c = (char)System.in.read(); <br>        numberHandler.handleRequest(c); <br>    } <br>} </pre>
<br>在組織物件之間的職責時，通常是從細粒度至粗粒度的方式來組織，從特殊到抽象化，就像程式中將數字視為字元的特殊化，字元又為符號的特殊化。 <br><br>Chain of Responsibility的 UML 結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title="Chain of Responsibility" style="WIDTH: 372px; HEIGHT: 217px" alt="Chain of Responsibility" src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/chainOfResponsibility-1.jpg"><br></div>
<br>從物件執行請求的時間來看，其運作是很簡單的職責傳遞而已，如下：<br>
<div style="TEXT-ALIGN: center"><img title="Chain of Responsibility" style="WIDTH: 370px; HEIGHT: 258px" alt="Chain of Responsibility" src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/chainOfResponsibility-2.jpg"><br></div>
<br>以上所舉的例子在請求上是很簡單的，只是比對輸入的型態，在更一般的情況下，可以將請求包裝為一個物件，並提供getType()之間的方法，以讓 Chain of Responsibility中的物件進行比對，例如： <br>
<ul>
    <li>Request.java </li>
</ul>
<pre>public class Request{ <br>　　private String type; <br><br>    public Request(String type) { this.type=type; }<br>　　public String getType() { return type; }<br><br>　　public void execute(){ <br>            // 執行請求 <br>　　} <br>} <br></pre>
<br>在Gof的書中所舉的例子為輔助說明系統，在一個介面中希望使用者一定可以得到相關的說明主題，如果子元件有說明的話，就顯示相關說明，否則的話就轉發給包括它的容器元件或父元件，以保證使用者的輔助說明請求一定可以得到回應。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111181.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesson2005/" target="_blank">张金鹏</a> 2007-04-17 10:45 <a href="http://www.blogjava.net/jesson2005/articles/111181.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>