﻿<?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-细心!用心!耐心!-文章分类-Structural 模式 </title><link>http://www.blogjava.net/jesson2005/category/21670.html</link><description>吾非文人，乃市井一俗人也，读百卷书，跨江河千里，故申城一游； 
一两滴辛酸，三四年学业，五六点粗墨，七八笔买卖，九十道人情。</description><language>zh-cn</language><lastBuildDate>Wed, 18 Apr 2007 06:45:23 GMT</lastBuildDate><pubDate>Wed, 18 Apr 2007 06:45:23 GMT</pubDate><ttl>60</ttl><item><title>Design Pattern: Decorator 模式</title><link>http://www.blogjava.net/jesson2005/articles/111179.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111179.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111179.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111179.html</trackback:ping><description><![CDATA[在Java Swing中的JTextArea元件預設並沒有捲軸，因為設計人員認為捲軸的功能並不是一定需要的，而決定讓程式人員可以動態選擇是否增加捲軸功能，捲軸的功能是由JScrollPane元件提供，如果您要加入一個具有捲軸功能的JTextArea，您可以如下進行設計：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">JTextArea textArea = new JTextArea();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">JScrollPane scrollPane = new JScrollPane(textArea);</span><br></div>
&nbsp;<br>JScrollPane對JTextArea即是個容器，而它對JFrame來說又是個元件，可以如下這般將之加入JFrame中：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">getContentPane().add(scrollPane);</span><br></div>
&nbsp;<br>像這樣動態的為JTextArea加入功能的方法，我們可以使用Decorator模式來組織結構，您可以動態的為一個物件加入一些功能（像是為 JTextArea加上捲軸），而又不用修改JTextArea的功能。對JTextArea來說，JScrollPane就好像是一個捲軸外框，直接套在JTextArea上作裝飾，就好比您在照片上加上一個相框的意思。<br><br>先以上面這個例子來說明Decorator模式的一個實例：<br>
<div style="TEXT-ALIGN: center"><img title=Decorator style="WIDTH: 455px; HEIGHT: 255px" alt=Decorator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/decorator-1.jpg"><br></div>
<br>如上圖所示的，無論是TextView或是Decorator類別，它們都是VisualComponent的一個子類，也就是說它們都是一個可視元件，而Decorator類又聚合了VisualComponent，所以又可以當作TextView容器，ScrollDecorator類別實作了 Decorator類，它可能是這樣設計的：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public abstract class Decorator extends VisualComponent {</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; protected VisualComponent component;</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 Decorator(VisualComponent component) {</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; this.component = component;</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 draw() {</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; component.draw();</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">}</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">public class ScrollDecorator extends Decorator {</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 ScrollDecorator(VisualComponent component) {</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; super(component);</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 draw() {</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; super.draw();</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; scrollTo();</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 scrollTo() {</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"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">}</span><br></div>
&nbsp;<br>要將新功能套用至TextView上，可以這樣設計：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">ScrollDecorator scrollDecorator = </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; new ScrollDecorator(new TextView());</span><br></div>
&nbsp;<br>super.draw()會先呼叫component也就是TextView物件的draw()方法先繪製TextView，然後再進行 ScrollPanel的scrollTo()，也就是捲動的方法。在圖中也表示了一個BorderDecorator，它可能是這樣設計的：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class BorderDecorator extends Decorator {</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 BorderDecorator(VisualComponent component) {</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; super(component);</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 draw() {</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; super.draw();</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; drawBorder();</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 drawBorder() {</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"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">}</span><br></div>
&nbsp;<br>要將ScrollDecorator與BorderDecorator加至TextView上，我們可以這樣設計：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">BorderDecorator borderDecorator =</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; new BorderDecorator(</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; new ScrollDecorator(new TextView()));</span><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"></span><br></div>
<br>所以當BorderDecorator調用draw()方法時，它會先調用ScrollDecorator的draw()方法，而 ScrollDecorator的draw()方法又會先調用TextView的draw()方法，所以繪製的順序變成：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">TextDraw.draw();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">ScrollDecorator.scrollTo();</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">BorderDecorator.drawBorder();</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>
下圖為物件之間的調用關係：<br>
<div style="TEXT-ALIGN: center"><img title=Decorator style="WIDTH: 403px; HEIGHT: 71px" alt=Decorator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/decorator-2.jpg"><br></div>
<br>Decorator模式的 UML 結構圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=Decorator style="WIDTH: 486px; HEIGHT: 259px" alt=Decorator src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/decorator-3.jpg"><br></div>
<br>在Gof的書中指出另一個範例，它設計一個Stream抽象類，而有一個StreamDecorator類，Stream的子類有處理記憶體串流的 MemoryStream與FileStream，有各種方法可以處理串流，也許只是單純的處理字元，也許會進行壓縮，也許會進行字元轉換，最基本的處理可能是處理字元，而字元壓縮被視為額外的功能，這個時候我們可以使用裝飾模式，在需要的時候為Stream物件加上必要的功能，事實上在java.io中的許多輸入輸出物件，就是採取這樣的設計。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111179.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:44 <a href="http://www.blogjava.net/jesson2005/articles/111179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Flyweight 模式</title><link>http://www.blogjava.net/jesson2005/articles/111180.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111180.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111180.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111180.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111180.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111180.html</trackback:ping><description><![CDATA[在 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm">Gof 的書</a>中指出，Flyweight的目的在於運用共享技術，使得一些細粒度的物件可以共享。<br><br>Flyweight在牛津字典中的解釋是"boxer of the lightest class"。意思是特輕量級拳擊手？其實應該是取"the lightest class"這部份的解釋，一個特輕量級類別，這個類別所產生的物件可以共用在每一個場合（context），並依場合資訊表現物件外觀。<br><br>在書中所舉出的例子是文檔編輯器中的字元物件，若每個字元物件會包括字元、大小、字型等等不同的資訊，想想一篇文章中可能出現多少字元，如果我們為每一個字元都使用一個物件來完整描述有關於它的訊息，那麼一篇文字中將會耗用多少的記憶體？！字元本身應可以共享，而大小、字型等等不同的資訊再分別設定。<br><br>考慮數量多且性質相近的物件時，將該物件的資訊分為兩個部份：內部狀態（intrinsic）與外部狀態（extrinsic）。以上例來說，字元屬於內部狀態，而大小、字型等等不同的資訊屬於外部狀態。<br><br>更詳細一些來說明，內部狀態是物件可共享的訊息部份，例如在繪製一個英文字串時，重覆的字元部份為內部狀態，像是 "ABC is BAC"，其中A、B、C的字元資訊部份不必直接儲存於字元物件中，它是屬於可以共享的部份，可以將這些可以重複使用的字元儲存在Flyweight Pool中。<br><br>外部狀態是物件依賴的一個場景（context），例如繪製字元時的字型資訊、位置資訊等等，繪製一個字元時，先從Flyweight Pool中找出共享的Flyweight，然後從場景中查找對應的繪製資訊（字型、大小、位置等）。<br><br>其實任何學過Java的人就一定使用過Java中運用Flyweight模式的好處，要知道，如果您在程式中使用下面的方式來宣告，則實際上是指向同一個字串物件：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">String str1 = "flyweight";</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">String str2 = "flyweight"; </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">System.out.println(str1 == str2);</span><br></div>
&nbsp;<br>程式的執行結果會顯示True，在Java中，會維護一個String Pool，對於一些可以共享的字串物件，會先在String Pool中查找是否存在相同的String內容（字元相同），如果有就直接傳回，而不是直接創造一個新的String物件，以減少記憶體的耗用。<br><br>再來個一看例子，String的intern()方法，我們來看看它的API說明的節錄：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">Returns a canonical representation for the string object.</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">A pool of strings, initially empty, is maintained privately by the class String.</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">When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.</span><br></div>
<br>這段話其實已說明了Flyweight模式的運作方式，用個實例來說明會更清楚：<br>
<ul>
    <li>Main.java </li>
</ul>
<pre>public class Main { <br>    public static void main(String[] args) { <br>        String str1 = "fly"; <br>        String str2 = "weight"; <br>        String str3 = "flyweight"; <br>        String str4; <br><br>        str4 = str1 + str2; <br>        System.out.println(str3 == str4); <br><br>        str4 = (str1 + str2).intern(); <br>        System.out.println(str3 == str4); <br>    } <br>}</pre>
<br>在程式中第一次比較str3與str4物件是否為同一物件時，您知道結果會是false，而intern()方法會先檢查 String Pool中是否存在字元部份相同的字串物件，如果有的話就傳回，由於程式中之前已經有"flyweight"字串物件，intern()在String Pool中發現了它，所以直接傳回，這時再進行比較，str3與str4所指向的其實是同一物件，所以結果會是true。<br><br>Flyweight模式在傳回物件時，所使用的是工廠模式，使用者並不會知道物件被創造的細節，下圖是Flyweight模式的結構圖： <br>
<div style="TEXT-ALIGN: center"><img title=Flyweight style="WIDTH: 450px; HEIGHT: 265px" alt=Flyweight src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/flyweight-1.jpg"><br></div>
<br>之前舉的例子是針對物件的內部狀態所作的說明，那麼字型資訊等外部的設定呢？一兩個簡單的外部資訊設定可以直接寫死（hard code）在程式中，例如簡單的使用介面字型設定。<br><br>但如果是文書處理器呢？使用者設定字型、大小等資訊會是動態的呢？Gof書中將字型資訊作為是繪製字元的外部狀態，使用一個Context 物件來維護外部狀態資料庫，每次要繪製字元物件時，這個Context物件會被作為參數傳遞給字元物件，字元物件透過查找Context中的資料來獲得字型資訊，從而進行正確的場景繪製。<br><br>外部狀態維護與內部狀態之間的對應關係，在查找時，Gof書中所使用的是BTree?結構，由於查找必須花費時間，所以這也指出了使用Flyweight 模式所必須付出的代價：以時間換取空間。如何設計外部狀態的資料結構，以使得查找時間縮短，這是另一個重要的課題（不過就不是這篇文章要討論的課題了）。<br><br>補充：關於字元（內部狀態）及字型、大小（外部狀態）之間的對應問題通常不太需要程式設計人員的關心，因為通常可以找的到一些現成的圖型介面API，它們都設計好一些相關元件，直接使用就可以了。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111180.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:44 <a href="http://www.blogjava.net/jesson2005/articles/111180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Bridge 模式</title><link>http://www.blogjava.net/jesson2005/articles/111177.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:42:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111177.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111177.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111177.html</trackback:ping><description><![CDATA[在 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm">Gof 的書</a>中指到Bridge模式的目的：「將抽象部份與它的實現部份分離，使它們都可以獨立地變化。」<br><br>這句話寫得簡短，這邊再作進一步的解釋，抽象部份指的是行為方面定義，實現方面指的是與特定平台相依的代碼實現。<br><br>一個實際的例子在Gof書中有提到，假設您定義了一個IWindow介面，這個介面只定義一些抽象的繪圖行為，而不涉及平台的實作，今天您可以繼承這個類來開發適用於X Window的XWindow類，也可以繼承這個類來開發適用於Windows XP系統的WindowsXP類，為了善用系統資源，您在實作IWindow介面時，會將與系統相關的實現代碼撰寫在介面的實作中。<br>
<div style="TEXT-ALIGN: center"><img title=Bridge style="WIDTH: 208px; HEIGHT: 275px" alt=Bridge src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/bridge-1.jpg"><br></div>
<br>假設今天您繼承了IWindow介面撰寫了一個I3DWindow介面，當中擴充一個drawBox()方法用於3D圖形的繪製，簡單的說， I3DWindow介面擴充了抽象行為，為了讓實現I3DWindow的類別也能在XWindow與Windows XP兩個不同的系統中運行，您必須再度撰寫與系統相關的實現代碼。<br>
<div style="TEXT-ALIGN: center"><img title=Bridge style="WIDTH: 433px; HEIGHT: 375px" alt=Bridge src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/bridge-2.jpg"></div>
<br>簡單的說，抽象行為定義與平台相關實現混雜在一起了，為了將抽象部份與它的實現部份分離，使它們都可以獨立地變化，您可以使用以下的結構。<br>
<div style="TEXT-ALIGN: center"><img title=Bridge style="WIDTH: 635px; HEIGHT: 367px" alt=Bridge src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/bridge-3.jpg"></div>
<br>在上圖的右邊中，與平台相關的實現部份，被與右邊抽象行方面的發展分開了，左邊的抽象部份您可以一直發展下去，而不再因為綁定了平台特定實作方法，而使得整個結構越來越失去彈性。<br><br>Bridge模式的 UML 類別結構圖如下：<br>
<div style="TEXT-ALIGN: center"><img title=Bridge style="WIDTH: 473px; HEIGHT: 224px" alt=Bridge src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/bridge-4.jpg"></div>
<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111177.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:42 <a href="http://www.blogjava.net/jesson2005/articles/111177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Composite 模式</title><link>http://www.blogjava.net/jesson2005/articles/111178.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:42:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111178.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111178.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111178.html</trackback:ping><description><![CDATA[如果以繪圖為例的話，一個文字是一個繪圖元件，一個線段是一個繪圖元件，而一個長方形也是一個繪圖元件，這些繪圖元件可以組成一個圖片，如果將這個圖片也視作一個繪圖元件，則這麼遞迴繪圖下去，就可以組合成一個較大的、複雜的圖形元件，這樣的目的可以使用Composite模式來解決。<br><br>對於使用者而言，無論是文字、線段或長方形，甚至是組合後的圖片元件，它們都擁有一個共同的行為，使用者基本上並不會感覺出它們之間的操作有任何的不同，您可以拖曳、放大、縮小等等，這些行為都是一致的。<br><br>以 UML 來表示上面這個例子的結構： <br>
<div style="TEXT-ALIGN: center"><img title=Composite style="WIDTH: 486px; HEIGHT: 400px" alt=Composite src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/composite-1.jpg"><br></div>
<br>對於Text、Line與Rectangle而言，Picture是一個容器，然而Picture繼承Graphic類，所以它也是一個繪圖元件，您可以進一步的將Picture組合至另一個Picture中，以形成較大的、複雜的圖形。<br><br>組合模式的UML結構圖如下所示： <br>
<div style="TEXT-ALIGN: center"><img title=Composite style="WIDTH: 396px; HEIGHT: 378px" alt=Composite src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/composite-2.jpg"><br><br></div>
依此架構，您可以遞迴的組合圖形元件，例如：<br>
<div style="TEXT-ALIGN: center"><img title=Composite style="WIDTH: 305px; HEIGHT: 167px" alt=Composite src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/composite-3.jpg"><br></div>
<br>除了繪圖元件之外，一些具有層次性或組合性的物件也可以使用Composite模式，像是電路元件、設備元件等等，使用Composite模式可以大大減低這些元件設計的複雜度。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111178.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:42 <a href="http://www.blogjava.net/jesson2005/articles/111178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Adapter 模式 - Object Adapter</title><link>http://www.blogjava.net/jesson2005/articles/111175.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:41:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111175.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111175.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111175.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111175.html</trackback:ping><description><![CDATA[您的電腦是個舊電腦，新的滑鼠都在使用USB接口了，而您的電腦上並沒有USB，而只有一個PS2接口，這時您可以使用一個USB轉PS2的接頭作為轉換，這樣您的電腦就可以使用新滑鼠了（當然您也可以使用USB擴充卡，意思是相同的）。<br><br>類似的概念，有時候您想在原來的程式中加入一個外部元件，例如一個類別，但是這個類別與您目前所設計的程式在介面上並不一致，為了讓這個外部類與原程式的介面一致，您必須使用一個類別作為中介來配接它們，這時您可以採用Adapter模式。<br><br>舉個例子來說，在Java 1.0中有個Enumeration，您在這個版本發行之後，使用它來設計了一個MessageApplication，例如： <br>
<ul>
    <li>MessageApplication.java </li>
</ul>
<span style="FONT-FAMILY: Courier New,Courier,monospace"></span>
<pre>import java.util.*;<br><br>public class MessageApplication {<br>    public void showAllMessage(Enumeration enum) {<br>        Object msg;<br>        while(enum.hasMoreElements()) { <br>            msg = enum.nextElement();<br>            System.out.println(msg);<br>        }<br>    }     <br>}</pre>
<br>您的客戶端程式是這麼使用MessageApplication的：<br>
<ul>
    <li>MessageClient.java </li>
</ul>
<pre>import java.util.*;<br><br>public class MessageClient {<br>    private MessageApplication msgApp;<br><br>    public void run() {<br>        Vector vector = new Vector();<br>        for(int i = 0; i &lt; 10; i++)<br>            vector.addElement("物件 " + i);<br>        <br>        msgApp = new MessageApplication();<br>        msgApp.showAllMessage(vector.elements());<br>    }<br>    <br>    public static void main(String[] args) {<br>        MessageClient msgClient = new MessageClient();<br>        msgClient.run();<br>    }<br>} <br></pre>
<br>現在Java 1.2中新增了Iterator，您想要使用它的功能，但基本上您不想更動原來程式中已設計好的MessageApplication類別，這時候您可以使用Adapter模式，將Iterator的介面轉換為Enumeration相容，例如：<br>
<ul>
    <li>IteratorAdapter.java </li>
</ul>
<pre>import java.util.*;<br><br>public class IteratorAdapter implements Enumeration {<br>    private Iterator iterator;<br><br>    IteratorAdapter(Iterator iterator) {<br>        this.iterator = iterator;   <br>    }<br><br>    // 轉接介面<br>    public boolean hasMoreElements() {<br>        return iterator.hasNext();<br>    }<br><br>    public Object nextElement() <br>                        throws NoSuchElementException {<br>        return iterator.next();<br>    } <br>} <br></pre>
<br>您可以在客戶端程式中照樣使用MessageApplication類別，而不用作任何的變動：<br>
<ul>
    <li>MessageClient.java </li>
</ul>
<pre>import java.util.*;<br><br>public class MessageClient {<br>    // We could still use MessageApplication<br>    private Enumeration iteratorAdapter;<br>    <br>    public void run() {<br>        List arrayList = new ArrayList();<br><br>        for(int i = 0; i &lt; 10; i++)<br>            arrayList.add("物件 " + i);<br>        <br>        iteratorAdapter = <br>               new IteratorAdapter(arrayList.iterator());<br>        // We could still use MessageApplication<br>        MessageApplication msgApp = new MessageApplication();       <br>        msgApp.showAllMessage(iteratorAdapter);<br>    }<br><br>    public static void main(String[] args) {<br>        MessageClient msgClient = new MessageClient();<br>        msgClient.run();<br>    }<br>} <br></pre>
<p>&#160;</p>
如程式所示的，透過Adapter模式，您原有程式中已設計好的類別不用更動，就可以引進新類別的功能，將上面的程式<span style="FONT-STYLE: italic"></span><cite class=urllink></cite>UML類別結構畫出如下：<br>
<div style="TEXT-ALIGN: center"><img title=Adapter style="WIDTH: 408px; HEIGHT: 150px" alt=Adapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/adapter-1.jpg"><br></div>
<br>上面的作法，是將要引進的新類別當作Adapter類別的一個物件成員，這是IbObject Adapter模式，其抽象結構如下：<br>
<div style="TEXT-ALIGN: center"><img title=Adapter style="WIDTH: 345px; HEIGHT: 176px" alt=Adapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/adapter-2.jpg"></div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111175.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:41 <a href="http://www.blogjava.net/jesson2005/articles/111175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Adapter 模式 - Class Adapter</title><link>http://www.blogjava.net/jesson2005/articles/111176.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:41:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111176.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111176.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111176.html</trackback:ping><description><![CDATA[Adapter模式的另一種作法是Class Adapter模式，在這個模式下，Adapter直接繼承Adaptee（要引進的新類別），以擁有當中的成員及方法，在C++中的話可以這麼作：<br>
<div style="TEXT-ALIGN: center"><img title=Adapter style="WIDTH: 343px; HEIGHT: 217px" alt=Adapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/adapter-3.jpg"><br></div>
<br>C++中可以多重繼承，但在Java中不行，所以在Java中若要採用Class Adapter，必須作點修改，一方面繼承Adaptee，一方面實作Target的介面：<br>
<div style="TEXT-ALIGN: center"><img title=Adapter style="WIDTH: 365px; HEIGHT: 224px" alt=Adapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/adapter-4.jpg"><br><br>
<div style="TEXT-ALIGN: left">代碼的實現是這樣的：&nbsp;<br></div>
<div style="TEXT-ALIGN: left">
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class Adapter extends Adaptee implements Target {</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">}</span><br></div>
<br>當然，這必須您原先的Target定義了共同的介面，所以Class Adapter在Java中適用的場合較少，事實上，也比較建議使用Object Adapter，這樣的Adapter模式比較有彈性，例如，您可以在Adapter上加上個setter，以隨時可以抽換Adaptee。<br><br>在Java中，Class Adapter的一個應用場合是達到多重繼承的效果，您一定在很多時候聽別人說，介面（interface）可以達到多重繼承的效果，這是怎麼回事？<br><br>其實要討論這個問題，首先您對於C++中多重繼承要先有認識，新手看了書說介面可以達到多重繼承，切莫人云亦云，尤其是沒有學過C++的新手們，如果您對於C++多重繼承想要有所認識，請先看看 <a href="http://caterpillar.onlyfun.net/Gossip/CppGossip/MultiInheritance1.html">多重繼承（一）</a>與 <a href="http://caterpillar.onlyfun.net/Gossip/CppGossip/MultiInheritance2.html">多重繼承（二）</a>。<br><br>Java不能多重繼承，但為何說Java中可以使用介面（interface）來達到多重繼承的效果，首先效果之一，就如 <a href="http://caterpillar.onlyfun.net/Gossip/CppGossip/MultiInheritance2.html">多重繼承（二）</a> 中描述的「多重繼承時通常其中一個基底類別作為private實作體，而其它的用以表現完全的抽象介面。」，在Java中這個效果可以使用介面來達到，介面此時所扮演的即 <a href="http://caterpillar.onlyfun.net/Gossip/CppGossip/MultiInheritance2.html">多重繼承（二）</a> 中的抽象類別，一個完全的抽象介面，這個效果的達成方式，如 <a href="http://caterpillar.onlyfun.net/Gossip/JavaGossip-V1/InterfaceType.htm">介面（interface）型態</a> 中所介紹的，您可以直接對應這兩個主題中的程式實作來瞭解，瞭解Java中如何使用介面（interface）來達到C++中所謂多重繼承的「一種」效果。<br><br>來看看另一個情況。<br><br>如果有SomeClass類別與OtherClass類別，您想要SomeAndOther類別可以同時擁有SomeClass類別與 OtherClass類別中已定義好的操作，並可以進行多型操作，在C++中可以用多重繼承來達到，但在Java中顯然的無法使用多重繼承，怎麼辦？您可以在設計上先繞個彎，先使用兩個介面分別定義好SomeClass與OtherClass兩個類別的公開方法，例如：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public interface ISome {</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; public void doSome();</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 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">public interface IOther {</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; public void doOther();</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>
<br>接著讓SomeClass與OtherClass分別實作兩個介面：<br>
<div style="MARGIN-LEFT: 40px; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold">public class SomeClass implements ISome {</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp; &nbsp; public void doSome() {</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp; &nbsp; &nbsp; &nbsp; ....</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp; &nbsp; }</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">}</span><br></div>
<br style="FONT-FAMILY: Courier New,Courier,monospace">
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class OtherClass implements IOther {</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; public void doOther() {</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; ....</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>
<br>SomeAndOther如何同時擁有兩個SomeClass與OtherClass類別已定義好的操作？並可以多型操作？SomeAndOther可以繼承其中之一，並擁有其中之一，例如：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class SomeAndOther extends SomeClass implements IOther {</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; private IOther other = new OtherClass();</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; public void doOther() {</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; other.doOther();</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>
<br>雖不滿意，但至少解決了目前的問題，當然這邊只是其中一例，畢竟C++是C++，Java是Java，兩者語法並不是一對一的關係，視實際需求還可以變化一下。<br></div>
</div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111176.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:41 <a href="http://www.blogjava.net/jesson2005/articles/111176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Default Adapter 模式</title><link>http://www.blogjava.net/jesson2005/articles/111174.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:40:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111174.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111174.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111174.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111174.html</trackback:ping><description><![CDATA[在Java中如果要定義事件處理的方式，必須實作EventListener的子介面，例如實作 WindowListener來定義一些視窗事件的處理方式，WindowListener中定義了七個方法：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public interface WindowListener extends EventListener {</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 windowOpened(WindowEvent 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; public void windowClosing(WindowEvent 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; public void windowClosed(WindowEvent 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; public void windowIconified(WindowEvent 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; public void windowDeiconified(WindowEvent 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; public void windowActivated(WindowEvent 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; public void windowDecativated(WindowEvent e);</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>可以定義一個類別來實作這個介面，以完全想要的事件處理，例如：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class WindowEventHandler implements WindowListener {</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 windowOpened(WindowEvent 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; public void windowClosing(WindowEvent e) {}</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 windowClosed(WindowEvent 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; System.exit(0);</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 windowIconified(WindowEvent 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; public void windowDeiconified(WindowEvent 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; public void windowActivated(WindowEvent 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; public void windowDecativated(WindowEvent e) {}</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><br>然而這有個缺點，實作介面的原則是您必須實作當中所定義的所有方法，即使您對某些事件並不感興趣，您也必須實作一個沒有內容的空方法，代表您已經實作了介面中定義的方法，然而有時，您並不知道介面中到底定義了幾個方法，或是知道也不知道方法的確切名稱與參數，即使您查了API，在程式中寫下一堆沒有實作內容的方法也是很煩人的一件事。<br><br>WindowAdapter類別預先實作了WindowListener介面，每個方法中都是空的實作，如下所示：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public abstract class WindowAdapter </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; implements WindowListener {</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 windowOpened(WindowEvent 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; public void windowClosing(WindowEvent 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; public void windowClosed(WindowEvent 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; public void windowIconified(WindowEvent 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; public void windowDeiconified(WindowEvent 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; public void windowActivated(WindowEvent 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; public void windowDecativated(WindowEvent e) {}</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>可以繼承WindowAdapter類別，並重新定義一些您所感興趣的事件，如此一來，就可以避開之前所提及的，直接實作 WindowListener介面的缺點，如下所示：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">public class WindowEventHandler extends WindowAdapter {</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 windowClosed(WindowEvent 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; System.exit(0);</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">}</span><br></div>
&nbsp;<br><br>這就是Default Adapter模式，它使用一個中介的Adapter類別來將真正感興趣的事件實作類別，配接至事件處理介面，上面的程式其 UML 圖如下：<br>
<div style="TEXT-ALIGN: center"><img title=DefaultAdapter style="WIDTH: 159px; HEIGHT: 211px" alt=DefaultAdapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/defaultAdapter-1.jpg"><br></div>
<br>將上圖一般化，Default Adapter模式的結構如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=DefaultAdapter style="WIDTH: 127px; HEIGHT: 143px" alt=DefaultAdapter src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/defaultAdapter-2.jpg"></div>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111174.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:40 <a href="http://www.blogjava.net/jesson2005/articles/111174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Proxy 模式（二）</title><link>http://www.blogjava.net/jesson2005/articles/111171.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111171.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111171.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111171.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111171.html</trackback:ping><description><![CDATA[延續 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/ProxyPattern.htm">Proxy模式（一）</a> 的議題，來看看實現代理的兩種方式：Static Proxy與Dynamic Proxy。嚴格來說這是屬於模式的實現方式，不過藉由實例可以更瞭解Proxy模式的應用。<br><br>先來看個例子，這個例子是記錄（log）動作，程式中很常需要為某些動作或事件作下記錄，以便在事後檢視或是作為除錯時的資訊，一個最簡單的例子如下： <br>
<ul>
    <li>HelloSpeaker.java </li>
</ul>
<pre>import java.util.logging.*; <br><br>public class HelloSpeaker { <br>    private Logger logger = <br>               Logger.getLogger(this.getClass().getName()); <br><br>    public void hello(String name) { <br>        logger.log(Level.INFO, "hello method starts....");<br><br>        System.out.println("Hello, " + name); <br><br>        logger.log(Level.INFO, "hello method ends...."); <br>    } <br>} </pre>
<br>HelloSpeaker在執行hello()方法時，您希望能記錄該方法已經執行及結束，最簡單的作法就是如上在執行的前後加上記錄動作，然而 Logger介入了HelloSpeaker中，記錄這個動作並不屬於HelloSpeaker，這使得HelloSpeaker增加了非業務上需要的邏輯在當中。<br><br>想想如果程式中這種記錄的動作到處都有需求，上面這種寫法勢必造成必須複製記錄動作的程式碼，使得維護記錄動作的困難度加大。如果不只有記錄動作，有一些非物件本身職責的相關動作也混入了物件之中（例如權限檢查、事務管理等等），會使得物件的負擔更形加重，甚至混淆了物件的職責，物件本身的職責所佔的程式碼，或許遠小於這些與物件職責不相關動作的程式碼。<br><br>怎麼辦，用下面的方法或許好一些，先定義一個介面，然後實作該介面： <br>
<ul>
    <li>IHello.java </li>
</ul>
<pre>public interface IHello { <br>    public void hello(String name); <br>} <br></pre>
<br>
<ul>
    <li>HelloSpeaker.java </li>
</ul>
<pre>public class HelloSpeaker implements IHello { <br>    public void hello(String name) { <br>        System.out.println("Hello, " + name); <br>    } <br>} <br></pre>
<br>接下來實作一個代理物件HelloProxy：<br>
<ul>
    <li>HelloProxy.java </li>
</ul>
<pre>import java.util.logging.*; <br><br>public class <span class=createlink>HelloProxy</span> implements <span class=createlink>IHello</span> { <br>    private Logger logger = <br>              Logger.getLogger(this.getClass().getName()); <br>    private <span class=createlink>IHello</span> helloObject; <br><br>    public <span class=createlink>HelloProxy</span>(<span class=createlink>IHello</span> helloObject) { <br>        this.helloObject = helloObject; <br>    } <br><br>    public void hello(String name) { <br>        logger.log(Level.INFO, "hello method starts...."); <br><br>        helloObject.hello(name); <br><br>        logger.log(Level.INFO, "hello method ends...."); <br>    } <br>}<br></pre>
<br>執行時可以如此：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">IHello helloProxy = new HelloProxy(new HelloSpeaker()); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">helloProxy.hello("Justin");</span><br></div>
<br>代理物件HelloProxy將代理真正的HelloSpeaker來執行hello()，並在其前後加上記錄的動作，這使得 HelloSpeaker在撰寫時不必介入記錄動作，HelloSpeaker可以專心於它的職責。<br><br>這是Static Proxy的基本範例，然而如您所看到的，代理物件的一個介面只服務於一種類型的物件，而且如果要代理的方法很多，勢必要為每個方法進行代理， Static Proxy在程式規模稍大時就必定無法勝任。<br><br>Java在JDK 1.3之後加入協助開發Dynamic Proxy功能的類別，我們不必為特定物件與方法撰寫特定的代理，使用Dynamic Proxy，可以使得一個handler服務於各個物件，首先，一個handler必須實現 java.lang.reflect.InvocationHandler： <br>
<ul>
    <li>LogHandler.java </li>
</ul>
<pre>import java.util.logging.*; <br>import java.lang.reflect.*; <br><br>public class LogHandler implements InvocationHandler { <br>    private Logger logger = <br>               Logger.getLogger(this.getClass().getName()); <br>    private Object delegate; <br><br>    public Object bind(Object delegate) { <br>        this.delegate = delegate; <br>        return Proxy.newProxyInstance(<br>                 delegate.getClass().getClassLoader(), <br>                 delegate.getClass().getInterfaces(), <br>                 this); <br>    }<br> <br>    public Object invoke(Object proxy, <br>                         Method method, <br>                         Object[] args) throws Throwable {<br>        Object result = null; <br>        try { <br>            logger.log(Level.INFO, <br>                         "method starts..." + method); <br>            result = method.invoke(delegate, args); <br>            logger.log(Level.INFO, <br>                         "method ends..." + method); <br>        } catch (Exception e){ <br>            logger.log(Level.INFO, e.toString()); <br>        } <br>        return result; <br>    } <br>} <br></pre>
<br>InvocationHandler的invoke()方法會傳入被代理物件的方法名稱與執行參數實際上要執行的方法交由method.invoke ()，並在其前後加上記錄動作，method.invoke()傳回的物件是實際方法執行過後的回傳結果。 <br><br>Dynamic Proxy必須宣告介面，實作該介面，例如： <br>
<ul>
    <li>IHello.java </li>
</ul>
<pre>public interface IHello { <br>    public void hello(String name); <br>} <br></pre>
<br>
<ul>
    <li>HelloSpeaker.java </li>
</ul>
<pre>public class HelloSpeaker implements IHello { <br>    public void hello(String name) { <br>        System.out.println("Hello, " + name); <br>    } <br>} <br></pre>
<br>java.lang.reflect.Proxy的newProxyInstance()依要代理的物件、介面與handler產生一個代理物件，我們可以使用下面的方法來執行程式：<br>
<div style="MARGIN-LEFT: 40px"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">LogHandler logHandler = new LogHandler(); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">IHello helloProxy = (IHello) logHandler.bind(</span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">new HelloSpeaker()); </span><br style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace"><span style="FONT-WEIGHT: bold; FONT-FAMILY: Courier New,Courier,monospace">helloProxy.hello("Justin");</span><br></div>
<br>LogHandler不在服務於特定物件與介面，而HelloSpeaker也不用插入任何有關於記錄的動作，它不用意識到記錄動作的存在。<br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111171.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:38 <a href="http://www.blogjava.net/jesson2005/articles/111171.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Facade 模式</title><link>http://www.blogjava.net/jesson2005/articles/111173.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111173.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111173.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111173.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111173.html</trackback:ping><description><![CDATA[考慮您要撰寫一個Web Mail程式，您手上已經有一些已經開發好的元件（Component），像是開發Web Mail所需要的SMTP處理類、允許上傳附檔的FileUpload類，以及Web安全相關的API、其它相關的Package等等。<br><br>當您拿到一些現成的元件，並打算在您的應用程式中使用它們的話，您不應該直接將這些元件耦合至您的應用程式上，例如：<br><img title=Facade style="BORDER-RIGHT: 0px solid; BORDER-TOP: 0px solid; BORDER-LEFT: 0px solid; WIDTH: 444px; BORDER-BOTTOM: 0px solid; HEIGHT: 255px" alt=Facade src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/facade-1.jpg"><br><br>上面的作法會讓您的應用程式直接與多個元件耦合，這種作法不但與元件的依賴度高，日後要修改應用程式本身，或是更換掉依賴的元件都有困難。<br><br>您應該定義一個Facade介面，在當中定義好商務邏輯，例如寄送附件檔案的方法（method），然後在一個實現類中，組合您手邊的元件，使其達成您所希望的商務行為，例如：<br><img title=Facade style="BORDER-RIGHT: 0px solid; BORDER-TOP: 0px solid; BORDER-LEFT: 0px solid; WIDTH: 491px; BORDER-BOTTOM: 0px solid; HEIGHT: 382px" alt=Facade src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/facade-2.jpg"><br><br>如上圖所示的，您的應用程式本身將不直接依賴於您的元件，而是依賴一個介面，當您想要更改某個商務行為的實作時，只要修改實現的類就可以了，應用程式本身不用作任何修改。<br><br>Facade模式的結構如下：<br><img oncontextmenu="javascript:window.open('http://www.caterpillar.onlyfun.net/PmWiki/pub/uploads/DesignPattern/facade-3.jpg'); window.event.returnValue=false;" title=Facade style="BORDER-RIGHT: 0px solid; BORDER-TOP: 0px solid; BORDER-LEFT: 0px solid; WIDTH: 491px; BORDER-BOTTOM: 0px solid; HEIGHT: 377px" alt=Facade src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/facade-3.jpg" onload="javascript:if(this.width>screen.width-250) this.width=screen.width-250"><br><br>Facade模式隱藏了各個元件之間的合作行為，以及元件本身的操作與設定細節，固而必失去了一些直接操作元件的方便性，所以對於喜歡追求與操作細節的程式設計人員而言，不會很喜歡透過Facade來操作背後的元件，所以您的Facade介面設計，通常要在元件依賴性及元件的支接操作性之間作個平衡。<br><br>
<img src ="http://www.blogjava.net/jesson2005/aggbug/111173.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:38 <a href="http://www.blogjava.net/jesson2005/articles/111173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Pattern: Proxy 模式（一）</title><link>http://www.blogjava.net/jesson2005/articles/111170.html</link><dc:creator>张金鹏</dc:creator><author>张金鹏</author><pubDate>Tue, 17 Apr 2007 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/jesson2005/articles/111170.html</guid><wfw:comment>http://www.blogjava.net/jesson2005/comments/111170.html</wfw:comment><comments>http://www.blogjava.net/jesson2005/articles/111170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesson2005/comments/commentRss/111170.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesson2005/services/trackbacks/111170.html</trackback:ping><description><![CDATA[在 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/GoF.htm">Gof 的書</a>中對Proxy模式的目的給定為：為其它的物件提供一種代理，以控制對這個物件的訪問。由這句話所延伸出來的意思是，根據您的目的不同，您的代理物件將負有不同的責任，因為產生多種不同的代理情況。<br><br>根據不同的代理目的，而有不同的代理情況，在Gof中所舉的一個例子是Virtual Proxy，當中舉一個文檔中內嵌圖片的例子，假設您的圖片是在文檔分頁的後面，一開始您並不用直接載入圖片，而使用一個虛代理物件，代替圖片被載入，以求開啟一個文檔的時候，速度能夠加快。當您捲動文檔至該顯示圖片的頁數時，這時再載入圖片。<br>
<div style="TEXT-ALIGN: center"><img title=Proxy style="WIDTH: 369px; HEIGHT: 246px" alt=Proxy src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/proxy-1.jpg"><br></div>
<br>如上圖所示，當文檔被開啟時，ImageProxy物件代理Image物件被載入，在還沒捲動至圖片顯示處時，也就是還沒有調用 ImageProxy的draw()時，圖片並不會被載入，因而可以加速文檔的開啟與節省記憶體的使用；如果需要顯示圖片了，ImageProxy的 draw()會被調用，而這時才真正創建Image物件，以從硬碟中載入圖片。<br><br>Proxy模式的 UML 結構圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=Proxy style="WIDTH: 330px; HEIGHT: 237px" alt=Proxy src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/proxy-2.jpg"><br></div>
<br>在調用RealSubject的request()之前，Proxy物件也許會有一些預先處理的操作，就假設我們組織為preOperation()與 postOperation()好了，當客戶對Proxy發出request()請求後，一個可能的時序圖如下所示：<br>
<div style="TEXT-ALIGN: center"><img title=Proxy style="WIDTH: 321px; HEIGHT: 350px" alt=Proxy src="http://caterpillar.onlyfun.net/Gossip/DesignPattern/images/proxy-3.jpg"><br></div>
<br>您的preOperation()與postOperation()正決定了Proxy模式使用於何種情況，例如一個Remote Proxy的情況，可以為一個遠端真實物件提供一個局部代表；Protection Proxy控制對物件的訪問，您可以使用它來作不同級別、權限的存取控制；Cache Proxy為一個物件提供臨時的儲存，使得許多客戶端都能直接存取它，而不用對真實物件直接要求，只有在必要的時候更新這個臨時物件，或是讓客戶直接存取真實物件。
<img src ="http://www.blogjava.net/jesson2005/aggbug/111170.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:37 <a href="http://www.blogjava.net/jesson2005/articles/111170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>