<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>網站製作學習誌-程式開發</title>
<link>http://blog.roodo.com/jaceju/archives/cat_446361.html</link>
<description>
首頁
讀者留言版
管理介面












_uacct = &quot;UA-450710-1&quot;;
urchinTracker();
</description>
<language>zh-tw</language>
<generator>Roodo Blog System</generator>
<copyright>All Rights Reserved</copyright>
<atom:link href="http://blog.roodo.com/jaceju/archives/cat_446361.xml" rel="self" type="application/rss+xml" />
<item>
	<title>Bridge 模式的簡單範例</title>
	<description><![CDATA[
	今天跟 tokimeki 討論到 Bridge 模式的例子，這裡我做個簡單的記錄。
假設我們有軟體 (Software) 會透過印表機 (Printer) 做列印輸出，而軟體有文字編輯器 (輸出文字) 與影像編輯器 (輸出影像) ，然後印表機則可分做雷射、噴墨及點陣式。
在設計上，這兩者分別屬於不同的繼承體系，而 Software 又會利用到 Printer ，因此它們形成了 Bridge 關係，如下圖： 

把幾個討論心得整理如下：


由於 Printer 抽象類封裝了實作上的不同，因此 Software 的子類別不必瞭解 Printer 的實作，它們只要知道 Printer 提供哪些介面可用。這些子類別會依照自己的需要來組合這些介面，就算用不到也沒關係。


在 Software 的子類別可以加入屬於自己的特殊方法，因為它們通常會依場合直接被調用，所以這裡並不需要遵守 LSP 原則。


對 Printer 體系來說，它不需要知道誰來用它；但反過來 Software 體系因為需要 Printer 體系的支援，所以形成了單向耦合。


對 Software 體系來說，只要它需要新的功能，而且這些功能所需要的實作也在 Printer 抽象類支援的範圍之內，那麼它就可以直接調用 Printer 物件的方法，而不會影響 Printer 體系。


如果 Software 體系需要的新功能是 Printer 抽象類所沒有的，那麼還是可以在 Printer 抽象類裡加入新的方法，而實作則交給 Printer 的子類別完成。這樣的修改完全不會影響 Software 體系。 


更詳細的 Bridge 模式介紹可參考良葛格的「 Design Pattern: Bridge 模式」一文。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>今天跟 <a href="http://blog.pixnet.net/HACGIS">tokimeki</a> 討論到 Bridge 模式的例子，這裡我做個簡單的記錄。</p>
<p>假設我們有軟體 (Software) 會透過印表機 (Printer) 做列印輸出，而軟體有文字編輯器 (輸出文字) 與影像編輯器 (輸出影像) ，然後印表機則可分做雷射、噴墨及點陣式。</p>
<p>在設計上，這兩者分別屬於不同的繼承體系，而 Software 又會利用到 Printer ，因此它們形成了 Bridge 關係，如下圖： </p>
<p><img src="http://www.jaceju.net/resources/bridge/PrinterSample.png" alt="Bridge 範例" width="817" height="357" /></p>
<p>把幾個討論心得整理如下：</p>
<ol>
<li>
<p>由於 Printer 抽象類封裝了實作上的不同，因此 Software 的子類別不必瞭解 Printer 的實作，它們只要知道 Printer 提供哪些介面可用。這些子類別會依照自己的需要來組合這些介面，就算用不到也沒關係。</p>
</li>
<li>
<p>在 Software 的子類別可以加入屬於自己的特殊方法，因為它們通常會依場合直接被調用，所以這裡並不需要遵守 LSP 原則。</p>
</li>
<li>
<p>對 Printer 體系來說，它不需要知道誰來用它；但反過來 Software 體系因為需要 Printer 體系的支援，所以形成了單向耦合。</p>
</li>
<li>
<p>對 Software 體系來說，只要它需要新的功能，而且這些功能所需要的實作也在 Printer 抽象類支援的範圍之內，那麼它就可以直接調用 Printer 物件的方法，而不會影響 Printer 體系。</p>
</li>
<li>
<p>如果 Software 體系需要的新功能是 Printer 抽象類所沒有的，那麼還是可以在 Printer 抽象類裡加入新的方法，而實作則交給 Printer 的子類別完成。這樣的修改完全不會影響 Software 體系。 </p>
</li>
</ol>
<p>更詳細的 Bridge 模式介紹可參考良葛格的「 <a href="http://caterpillar.onlyfun.net/Gossip/DesignPattern/BridgePattern.htm">Design Pattern: Bridge 模式</a>」一文。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/5883189.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/5883189.html</guid>
	<category>程式開發</category>
	<pubDate>Fri, 18 Apr 2008 23:37:06 +0800</pubDate>
</item>
<item>
	<title>[SVN] 如何同時讓多個資料夾與外部 SVN 同步？</title>
	<description><![CDATA[
	遇到了一個 Subversion 的問題，記下來免得以後忘記。
問題說明
現在我有一個專案用到了 Zend Framework 和我自己維護的一個 MyLib 類別庫，而我想要讓它們都維持與官方 SVN 同步時怎麼做呢？
假設專案目錄結構如下：
app/
    lib/
        Zend/
        MyLib/
現在我要讓 Zend 這個資料夾與 http://framework.zend.com/svn/framework/trunk/library/Zend 同步，而 MyLib 則是與 http://localhost/svn/mylib/trunk 同步。
解決方案
Subversion 有個很用好的屬性稱為 svn:externals ，它可以用來指定讓某個目錄與外部的 SVN 同步，只是我之前僅會用到一個目錄的同步而已。例如我可以在 lib 目錄按下滑鼠右鍵，選擇 TortoiseSVN 的性質；然後在性質視窗建立一個 svn:externals 屬性，其值為： 
Zend http://framework.zend.com/svn/framework/trunk/library/Zend
粗體字即為目錄名稱，然後空一格後面接著外部 SVN 的位置。
那麼如果要再多一個外部 SVN 的時候怎麼辦呢？我們只要在編輯原來的 svn:externals 屬性，並在原值的下方再加入新的設定值，也就是： 
Zend http://framework.zend.com/svn/framework/trunk/library/Zend (換行) 
MyLib http://localhost/svn/mylib/trunk 
即新增粗體字的部份即可。
最後再利用 svn 更新一次，這樣就會讓 Zend 和 MyLib 資料夾和外部 SVN 同步了。 

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>遇到了一個 Subversion 的問題，記下來免得以後忘記。</p>
<h2>問題說明</h2>
<p>現在我有一個專案用到了 Zend Framework 和我自己維護的一個 MyLib 類別庫，而我想要讓它們都維持與官方 SVN 同步時怎麼做呢？</p>
<p>假設專案目錄結構如下：</p>
<pre><code>app/
    lib/
        Zend/
        MyLib/</code></pre>
<p>現在我要讓 Zend 這個資料夾與 http://framework.zend.com/svn/framework/trunk/library/Zend 同步，而 MyLib 則是與 http://localhost/svn/mylib/trunk 同步。</p>
<h2>解決方案</h2>
<p>Subversion 有個很用好的屬性稱為 svn:externals ，它可以用來指定讓某個目錄與外部的 SVN 同步，只是我之前僅會用到一個目錄的同步而已。例如我可以在 lib 目錄按下滑鼠右鍵，選擇 TortoiseSVN 的性質；然後在性質視窗建立一個 svn:externals 屬性，其值為： </p>
<pre><code><strong>Zend</strong> http://framework.zend.com/svn/framework/trunk/library/Zend</code></pre>
<p>粗體字即為目錄名稱，然後空一格後面接著外部 SVN 的位置。</p>
<p>那麼如果要再多一個外部 SVN 的時候怎麼辦呢？我們只要在編輯原來的 svn:externals 屬性，並在原值的下方再加入新的設定值，也就是： </p>
<pre><code>Zend http://framework.zend.com/svn/framework/trunk/library/Zend (換行) 
<strong>MyLib http://localhost/svn/mylib/trunk </strong></code></pre>
<p>即新增粗體字的部份即可。</p>
<p>最後再利用 svn 更新一次，這樣就會讓 Zend 和 MyLib 資料夾和外部 SVN 同步了。 </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/4280181.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/4280181.html</guid>
	<category>程式開發</category>
	<pubDate>Wed, 10 Oct 2007 11:02:43 +0800</pubDate>
</item>
<item>
	<title>[PHP] 該用 Abstract Class 還是 Interface ？</title>
	<description><![CDATA[
	這裡把自己對 Abstract Class (抽象類別) 和 Interface (介面) 的理解記一下，如果有錯請指正我 :)
Abstract Class 與 Interface 的宣告方式 
 Abstract Class 和 Interface 本身都無法用來建構一個 object (物件實體) ，它們必須透過其他類別以 extends (繼承) 或 implements (實作) 來完成目的。在 PHP 中，我們可以用以下的方式來宣告一個 Abstract Class ：
abstract class Test
{
    // ... code ...
} 
然後其 sub class 必須用 extends 來繼承它：
class ClassA extends Test
{
    // ... code ...
} 
而 Interface 則是用以下的方式宣告：
interface Testable
{
    // ... code ...
} 
然後 sub class 類別便可以 implements 關鍵字來實作該介面：
class ClassB implements Testable
{
    // ... code ...
} 
註：同人老大說實作 Interface 的類別不能稱為 sub class ，不過我實在也想不出什麼好名詞，歡迎大家提供意見。 
另外比較特別的是， Interface 可以繼承衍生自另一個 Interface ：
interface A extends B
{
    // ... code ...
}
這裡本來用繼承一詞，經同人老大指點後，用衍生一詞會比較精準。
但是除了語法上的不同，它們本身所代表的意義是什麼呢？
Abstract Class
當整個類別繼承體系有一致的行為時，我們通常會將這些行為抽離到上一層的 super class 去。如果這些行為的程式碼不在 super class 中定義的話，那麼會使得其 sub class 必須重覆地出現相同的程式片段，導致了壞味道的發生。然而有時我們並不打算讓這些 super class 可以被建構成物件實體，那麼它們就是所謂的 Abstract Class 。 
在 Abstract Class 中， method 的定義方式有兩種，以下是有實作的 method ：
public function method1()
{
    // ... code ...
}
值得注意的是，就算大括號中沒有程式碼，也算是有實作的 method ，我稱之為空實作。 
另一種是抽象方法，只要在 function 前加上 abstract 關鍵字，再拿掉大括號並加上分號：
abstract public function method2();
這樣的抽象方法，就會要求 sub class 在繼承之後要實作該方法的細節。 
Interface
在我們確定了物件之間的溝通方式及規格，卻未能確定其實作細節時，就是利用 Interface 的時機。 Interface 能保證 Client 在操作物件上有一致的方式，而具體的表現方式則是讓實作的類別來決定。因此這些類別都必須實作在 Interface 裡定義的 method ，否則將會出現錯誤。在 Interface 中所有方法都是 abstract 的，定義方式和 Abstract Class 的抽象方法一樣，但是不用加上 abstract 關鍵字，例如：
public function method3();
顯然地，當 Abstract Class 所擁有的方法都是 abstract method 時，它就退化成了一個 Interface 。不過還是要小心以下兩點：


一個 Class 可以實作多個 Interface ，但只能繼承一個 Abstract Class ；這即是所謂的單一繼承體系，也就是子類別只能繼承一個父類別；但是一個父類別則可以被多個子類別所繼承。


在 Abstract Class 中可以宣告屬性成員 (attribute) 而 Interface 是不可以的，但兩者都能有常數成員 (constant) 。 


範例
假設我們有兩個裝置 (Device) ，一個是鍵盤 (Keyboard) ，一個是滑鼠 (Mouse) ；而這兩種裝置都支援 USB 和 PS/2 兩種接頭 (Adapter) 讓使用者可以自行選擇，不過一次只能選擇一種接頭。 
註：這個範例舉得不是很好，只是為了說明而已。
上面文字明白指出了我們可能會有 Device 和 Adapter 兩個 Abstract Class (或 Interface ) ，不過問題是哪一個應該用 Abstract Class ？而哪一個該用 Interface 呢？
為了簡化說明，我們把原來的問題重新定義為以下的規格：


 Device 有 inputData (輸入資料) 及 getStatus (取得狀態) 等兩個方法。


 Adapter 有 send (傳送) 和 receive (接收) 兩個方法。


 Device 必須透過 Adapter 來傳送或接收資料， 


我們從規格的第三點可以得知，無論 Device 的類型為何，都會需要透過 Adapter 來收送資料，這就是一種抽象行為。也因此我們必須為 Device 提供一個 Adapter 類型的 $_adapter 屬性成員，從這點就可以看出 Device 應該是個 Abstract Class 。
而因為有 $_adapter 屬性，但我們又不想讓外界直接改變它，所以我們將它的 scope 設置為 private  (私有屬性) ；然而要設定 private attribute 我們就要借重一個規格外的方法，這裡我將它命名為 setAdapter ；所以當我們在呼叫 setAdapter 時就必須傳入一個 Adapter 物件來指定給 $_adapter 屬性，這樣就能防止 Device 在使用 $_adapter 時的錯誤。

    public function setAdapter(Adapter $adapter)
    {
        $this-&gt;_adapter = $adapter;
    }
然後 Device 的 inputData 和 getStatus 就會委託 Adapter 物件的 send 及 receive 來收送資料：
    public function inputData($data)
    {
        echo $this-&gt;_deviceName, ' input data:';
        $this-&gt;_adapter-&gt;send($data);
    }

    public function getStatus()
    {
        echo $this-&gt;_deviceName, ' get status:';
        echo $this-&gt;_adapter-&gt;receive();
    }
註：以上程式還是有一些潛在的小問題，這裡就留給各位自行判斷囉。
對 Device 的 sub class 來說，以上的行為都是可以共用，也因此不必再實作一次了。完整的 Device 類別體系如下：
&lt;?php

abstract class Device
{
    private $_adapter = null;

    protected $_deviceName = '';

    public function setAdapter(Adapter $adapter)
    {
        $this-&gt;_adapter = $adapter;
    }

    public function inputData($data)
    {
        echo $this-&gt;_deviceName, ' input data:';
        $this-&gt;_adapter-&gt;send($data);
    }

    public function getStatus()
    {
        echo $this-&gt;_deviceName, ' get status:';
        echo $this-&gt;_adapter-&gt;receive();
    }
}

class Device_Keyboard extends Device
{
    protected $_deviceName = 'Keyboard';
}

class Device_Mouse extends Device
{
    protected $_deviceName = 'Mouse';
}
接著再看 Adapter ，因為 USB 和 PS/2 在規格實作上是有所差異的，因此我們無法在 Adapter 中直接去定義像 Device 中 inputData 及 getStatus 這樣共用的行為方法。所以在這裡我們就能將 Adapter 視為是一個 Interface ，讓其下的 Class 去實作 send 和 receive 兩個方法的細節。所以整個 Adapter 的類別體系如下： 
&lt;?php

interface Adapter
{
    public function send($data);

    public function receive();
}

class Adapter_Ps2 implements Adapter
{
    public function send($data)
    {
        echo $data, ' by PS2.', &quot;\n&quot;;
    }

    public function receive()
    {
        return rand(100, 200) . ' by PS2.' . &quot;\n&quot;;
    }
}

class Adapter_Usb implements Adapter
{
    public function send($data)
    {
        echo $data, ' by USB.', &quot;\n&quot;;
    }

    public function receive()
    {
        return rand(300, 400) . ' by USB.' . &quot;\n&quot;;
    }
}
註：為了說明方便，所以我簡化了 USB 和 PS/2 的兩個方法的實作方式。
當然 Adapter 也不一定要是 Interface 才行，這得看整個程式架構上的設計來判斷。如果在 Adpater 在設計上會有共用的行為或屬性時，那麼 Abstract Class 就是比較好的選擇；只是在這個範例裡，我為了說明 Interface 的緣故，就大幅簡化了 Adapter 的設計。
整個設計用 UML 來表示的話，就是以下這樣子：

以下是測試的程式：
&lt;?php

require_once 'Device.php';
require_once 'Adapter.php';

$keyboard = new Device_Keyboard();
$mouse    = new Device_Mouse();

echo &quot;\n&quot;;
$keyboard-&gt;setAdapter(new Adapter_Ps2());
$keyboard-&gt;inputData('abc');
$keyboard-&gt;getStatus();

echo &quot;\n&quot;;
$mouse-&gt;setAdapter(new Adapter_Ps2());
$mouse-&gt;inputData('def');
$mouse-&gt;getStatus();

echo &quot;\n&quot;;
$keyboard-&gt;setAdapter(new Adapter_Usb());
$keyboard-&gt;inputData('abc');
$keyboard-&gt;getStatus();

echo &quot;\n&quot;;
$mouse-&gt;setAdapter(new Adapter_Usb());
$mouse-&gt;inputData('def');
$mouse-&gt;getStatus();

/*
程式執行結果：
====================================================

Keyboard input data:abc by PS2.
Keyboard get status:130 by PS2.

Mouse input data:def by PS2.
Mouse get status:165 by PS2.

Keyboard input data:abc by USB.
Keyboard get status:360 by USB.

Mouse input data:def by USB.
Mouse get status:353 by USB.

====================================================
*/
完整範例下載
結論
很多時候我們常會搞不清楚該用 Abstract Class 還是 Interface ，其實這在設計階段是常有的事情。所以掌握以下的重點，就會比較容易看出兩者的使用時機：


當類別有共同的行為或屬性時，可以考慮使用 Abstract Class 。


當類別有共同的操作介面，但是實作上卻有所差異時，可以考慮使用 Interface 。 


不過當我們發現整個類別體系用錯 Abstract Class 或 Interface 時也不用過於煩惱，這時「 Refactoring (重構) 」就是我們會需要的好幫手。更詳細的 Refactoring 說明可以參考以下書籍：


Refactoring by Martin Fowler (中譯本：重構 by 侯捷、熊節)


Refactoring To Patterns by Joshua Kerievsky



	]]>
	</description>
	<content:encoded><![CDATA[
	<p>這裡把自己對 Abstract Class (抽象類別) 和 Interface (介面) 的理解記一下，如果有錯請指正我 :)</p>
<h2>Abstract Class 與 Interface 的宣告方式 </h2>
<p> Abstract Class 和 Interface 本身都無法用來建構一個 object (物件實體) ，它們必須透過其他類別以 extends (繼承) 或 implements (實作) 來完成目的。在 PHP 中，我們可以用以下的方式來宣告一個 Abstract Class ：</p>
<pre><code>abstract class Test
{
    // ... code ...
} </code></pre>
<p>然後其 sub class 必須用 extends 來繼承它：</p>
<pre><code>class ClassA extends Test
{
    // ... code ...
} </code></pre>
<p>而 Interface 則是用以下的方式宣告：</p>
<pre><code>interface Testable
{
    // ... code ...
} </code></pre>
<p>然後<del> sub class </del>類別便可以 implements 關鍵字來實作該介面：</p>
<pre><code>class ClassB implements Testable
{
    // ... code ...
} </code></pre>
<p class="note">註：同人老大說實作 Interface 的類別不能稱為 sub class ，不過我實在也想不出什麼好名詞，歡迎大家提供意見。 </p>
<p>另外比較特別的是， Interface 可以<del>繼承</del>衍生自另一個 Interface ：</p>
<pre><code>interface A extends B
{
    // ... code ...
}</code></pre>
<p class="note">這裡本來用繼承一詞，經同人老大指點後，用衍生一詞會比較精準。</p>
<p>但是除了語法上的不同，它們本身所代表的意義是什麼呢？</p>
<h2>Abstract Class</h2>
<p>當整個類別繼承體系有一致的行為時，我們通常會將這些行為抽離到上一層的 super class 去。如果這些行為的程式碼不在 super class 中定義的話，那麼會使得其 sub class 必須重覆地出現相同的程式片段，導致了壞味道的發生。然而有時我們並不打算讓這些 super class 可以被建構成物件實體，那麼它們就是所謂的 Abstract Class 。 </p>
<p>在 Abstract Class 中， method 的定義方式有兩種，以下是有實作的 method ：</p>
<pre><code>public function method1()
{
    // ... code ...
}</code></pre>
<p>值得注意的是，就算大括號中沒有程式碼，也算是有實作的 method ，我稱之為<strong>空實作</strong>。 </p>
<p>另一種是抽象方法，只要在 function 前加上 abstract 關鍵字，再拿掉大括號並加上分號：</p>
<pre><code>abstract public function method2();</code></pre>
<p>這樣的抽象方法，就會要求 sub class 在繼承之後要實作該方法的細節。 </p>
<h2>Interface</h2>
<p>在我們確定了物件之間的溝通方式及規格，卻未能確定其實作細節時，就是利用 Interface 的時機。 Interface 能保證 Client 在操作物件上有一致的方式，而具體的表現方式則是讓實作的類別來決定。因此這些類別都必須實作在 Interface 裡定義的 method ，否則將會出現錯誤。在 Interface 中所有方法都是 abstract 的，定義方式和 Abstract Class 的抽象方法一樣，但是不用加上 abstract 關鍵字，例如：</p>
<pre><code>public function method3();</code></pre>
<p>顯然地，當 Abstract Class 所擁有的方法都是 abstract method 時，它就退化成了一個 Interface 。不過還是要小心以下兩點：</p>
<ol>
<li>
<p>一個 Class 可以實作多個 Interface ，但只能繼承一個 Abstract Class ；這即是所謂的單一繼承體系，也就是子類別只能繼承一個父類別；但是一個父類別則可以被多個子類別所繼承。</p>
</li>
<li>
<p>在 Abstract Class 中可以宣告屬性成員 (attribute) 而 Interface 是不可以的，但兩者都能有常數成員 (constant) 。 </p>
</li>
</ol>
<h2>範例</h2>
<p>假設我們有兩個裝置 (Device) ，一個是鍵盤 (Keyboard) ，一個是滑鼠 (Mouse) ；而這兩種裝置都支援 USB 和 PS/2 兩種接頭 (Adapter) 讓使用者可以自行選擇，不過一次只能選擇一種接頭。 </p>
<p class="note">註：這個範例舉得不是很好，只是為了說明而已。</p>
<p>上面文字明白指出了我們可能會有 Device 和 Adapter 兩個 Abstract Class (或 Interface ) ，不過問題是哪一個應該用 Abstract Class ？而哪一個該用 Interface 呢？</p>
<p>為了簡化說明，我們把原來的問題重新定義為以下的規格：</p>
<ol>
<li>
<p> Device 有 inputData (輸入資料) 及 getStatus (取得狀態) 等兩個方法。</p>
</li>
<li>
<p> Adapter 有 send (傳送) 和 receive (接收) 兩個方法。</p>
</li>
<li>
<p> Device 必須透過 Adapter 來傳送或接收資料， </p>
</li>
</ol>
<p>我們從規格的第三點可以得知，無論 Device 的類型為何，都會需要透過 Adapter 來收送資料，這就是一種抽象行為。也因此我們必須為 Device 提供一個 Adapter 類型的 $_adapter 屬性成員，從這點就可以看出 Device 應該是個 Abstract Class 。</p>
<p>而因為有 $_adapter 屬性，但我們又不想讓外界直接改變它，所以我們將它的 scope 設置為 private  (私有屬性) ；然而要設定 private attribute 我們就要借重一個規格外的方法，這裡我將它命名為 setAdapter ；所以當我們在呼叫 setAdapter 時就必須傳入一個 Adapter 物件來指定給 $_adapter 屬性，這樣就能防止 Device 在使用 $_adapter 時的錯誤。</p>
<pre><code>
    <strong>public</strong> <strong>function</strong> setAdapter(Adapter $adapter)
    {
        $this-&gt;_adapter = $adapter;
    }</code></pre>
<p>然後 Device 的 inputData 和 getStatus 就會委託 Adapter 物件的 send 及 receive 來收送資料：</p>
<pre><code>    <strong>public</strong> <strong>function</strong> inputData($data)
    {
        <strong>echo</strong> $this-&gt;_deviceName, ' input data:';
        $this-&gt;_adapter-&gt;send($data);
    }

    <strong>public</strong> <strong>function</strong> getStatus()
    {
        <strong>echo</strong> $this-&gt;_deviceName, ' get status:';
        <strong>echo</strong> $this-&gt;_adapter-&gt;receive();
    }</code></pre>
<p class="note">註：以上程式還是有一些潛在的小問題，這裡就留給各位自行判斷囉。</p>
<p>對 Device 的 sub class 來說，以上的行為都是可以共用，也因此不必再實作一次了。完整的 Device 類別體系如下：</p>
<pre><code>&lt;?php

<strong>abstract</strong> <strong>class</strong> Device
{
    <strong>private</strong> $_adapter = null;

    <strong>protected</strong> $_deviceName = '';

    <strong>public</strong> <strong>function</strong> setAdapter(Adapter $adapter)
    {
        $this-&gt;_adapter = $adapter;
    }

    <strong>public</strong> <strong>function</strong> inputData($data)
    {
        <strong>echo</strong> $this-&gt;_deviceName, ' input data:';
        $this-&gt;_adapter-&gt;send($data);
    }

    <strong>public</strong> <strong>function</strong> getStatus()
    {
        <strong>echo</strong> $this-&gt;_deviceName, ' get status:';
        <strong>echo</strong> $this-&gt;_adapter-&gt;receive();
    }
}

<strong>class</strong> Device_Keyboard <strong>extends</strong> Device
{
    <strong>protected</strong> $_deviceName = 'Keyboard';
}

<strong>class</strong> Device_Mouse <strong>extends</strong> Device
{
    <strong>protected</strong> $_deviceName = 'Mouse';
}</code></pre>
<p>接著再看 Adapter ，因為 USB 和 PS/2 在規格實作上是有所差異的，因此我們無法在 Adapter 中直接去定義像 Device 中 inputData 及 getStatus 這樣共用的行為方法。所以在這裡我們就能將 Adapter 視為是一個 Interface ，讓其下的 Class 去實作 send 和 receive 兩個方法的細節。所以整個 Adapter 的類別體系如下： </p>
<pre><code>&lt;?php

interface Adapter
{
    public function send($data);

    public function receive();
}

class Adapter_Ps2 implements Adapter
{
    public function send($data)
    {
        echo $data, ' by PS2.', &quot;\n&quot;;
    }

    public function receive()
    {
        return rand(100, 200) . ' by PS2.' . &quot;\n&quot;;
    }
}

class Adapter_Usb implements Adapter
{
    public function send($data)
    {
        echo $data, ' by USB.', &quot;\n&quot;;
    }

    public function receive()
    {
        return rand(300, 400) . ' by USB.' . &quot;\n&quot;;
    }
}</code></pre>
<p class="note">註：為了說明方便，所以我簡化了 USB 和 PS/2 的兩個方法的實作方式。</p>
<p><strong>當然 Adapter 也不一定要是 Interface 才行，這得看整個程式架構上的設計來判斷。</strong>如果在 Adpater 在設計上會有共用的行為或屬性時，那麼 Abstract Class 就是比較好的選擇；只是在這個範例裡，我為了說明 Interface 的緣故，就大幅簡化了 Adapter 的設計。</p>
<p>整個設計用 UML 來表示的話，就是以下這樣子：</p>
<p class="image"><img src="http://www.jaceju.net/resources/abstract_class_or_interface/abstract_class_or_interface.png" alt="Device and Adapter" width="623" height="281" /></p>
<p>以下是測試的程式：</p>
<pre><code>&lt;?php

require_once 'Device.php';
require_once 'Adapter.php';

$keyboard = new Device_Keyboard();
$mouse    = new Device_Mouse();

echo &quot;\n&quot;;
$keyboard-&gt;setAdapter(new Adapter_Ps2());
$keyboard-&gt;inputData('abc');
$keyboard-&gt;getStatus();

echo &quot;\n&quot;;
$mouse-&gt;setAdapter(new Adapter_Ps2());
$mouse-&gt;inputData('def');
$mouse-&gt;getStatus();

echo &quot;\n&quot;;
$keyboard-&gt;setAdapter(new Adapter_Usb());
$keyboard-&gt;inputData('abc');
$keyboard-&gt;getStatus();

echo &quot;\n&quot;;
$mouse-&gt;setAdapter(new Adapter_Usb());
$mouse-&gt;inputData('def');
$mouse-&gt;getStatus();

/*
程式執行結果：
====================================================

Keyboard input data:abc by PS2.
Keyboard get status:130 by PS2.

Mouse input data:def by PS2.
Mouse get status:165 by PS2.

Keyboard input data:abc by USB.
Keyboard get status:360 by USB.

Mouse input data:def by USB.
Mouse get status:353 by USB.

====================================================
*/</code></pre>
<p><a href="http://www.jaceju.net/resources/abstract_class_or_interface/sample.zip">完整範例下載</a></p>
<h2>結論</h2>
<p>很多時候我們常會搞不清楚該用 Abstract Class 還是 Interface ，其實這在設計階段是常有的事情。所以掌握以下的重點，就會比較容易看出兩者的使用時機：</p>
<ol>
<li>
<p>當類別有共同的行為或屬性時，可以考慮使用 Abstract Class 。</p>
</li>
<li>
<p>當類別有共同的操作介面，但是實作上卻有所差異時，可以考慮使用 Interface 。 </p>
</li>
</ol>
<p>不過當我們發現整個類別體系用錯 Abstract Class 或 Interface 時也不用過於煩惱，這時「 Refactoring (重構) 」就是我們會需要的好幫手。更詳細的 Refactoring 說明可以參考以下書籍：</p>
<ul>
<li>
<p>Refactoring by Martin Fowler (中譯本：<a href="http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9867594061&amp;sid=17667">重構</a> by 侯捷、熊節)</p>
</li>
<li>
<p><a href="http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=0321293533&amp;sid=30226">Refactoring To Patterns</a> by Joshua Kerievsky</p>
</li>
</ul>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3996905.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3996905.html</guid>
	<category>程式開發</category>
	<pubDate>Fri, 24 Aug 2007 12:40:59 +0800</pubDate>
</item>
<item>
	<title>[轉載] Readable regular expressions</title>
	<description><![CDATA[
	上次介紹過 Regular Expression 五個良好的習慣，這次來個英文的相關文章。
範例是用 PHP ，不過我想其他語言應該也適用。
文章網址：Readable regular expressions (英文) 
	]]>
	</description>
	<content:encoded><![CDATA[
	<p>上次介紹過 <a href="http://blog.roodo.com/jaceju/archives/1480444.html">Regular Expression 五個良好的習慣</a>，這次來個英文的相關文章。</p>
<p>範例是用 PHP ，不過我想其他語言應該也適用。</p>
<p>文章網址：<a href="http://www.reiersol.com/blog/index.php?op=ViewArticle&articleId=18&blogId=1">Readable regular expressions</a> (英文) </p>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/2878669.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/2878669.html</guid>
	<category>程式開發</category>
	<pubDate>Mon, 19 Mar 2007 10:16:20 +0800</pubDate>
</item>
<item>
	<title>[轉載] RoR 風潮的背後</title>
	<description><![CDATA[
	近來 RoR 話題不斷，以下這篇文章可以讓大家看看反面的意見。至於觀點對不對，我不予評論 (其實沒用過 RoR 的我也沒這個資格評論) ；因為對我而言，技術沒有絕對的對錯，只有使用的時機與環境是不是恰當。
文章連結：RoR 風潮的背後 (簡體中文) 。
重點提示：
從某種程度上來說，它掩蓋了問題的實質：領域對象的價值！

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>近來 RoR 話題不斷，以下這篇文章可以讓大家看看反面的意見。至於觀點對不對，我不予評論 (其實沒用過 RoR 的我也沒這個資格評論) ；因為對我而言，技術沒有絕對的對錯，只有使用的時機與環境是不是恰當。</p>
<p>文章連結：<a href="http://sushener.spaces.msn.com/Blog/cns!BB54050A5CFAFCDD!596.entry">RoR 風潮的背後</a> (簡體中文) 。</p>
<p>重點提示：</p>
<blockquote>從某種程度上來說，它掩蓋了問題的實質：領域對象的價值！</blockquote>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/1869262.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/1869262.html</guid>
	<category>程式開發</category>
	<pubDate>Mon, 10 Jul 2006 09:47:54 +0800</pubDate>
</item>
<item>
	<title>[轉載] 正則表達式的五個成功習慣</title>
	<description><![CDATA[
	Regular Expression (正規表示式、常規表示式、正規表達式) 是很多程式開發者所懼怕的一種語法；但是因為它的功能強大，使得軟體界有個不成文的說法：沒學會 Regular Expression ，就不能稱為高手。
我是後來才學會 Regular Expression 怎麼寫的，不過我沒有因此而變成一個高手。但是 Regular Expression 讓我的程式功力大大的進步，而且在支援 Regular Expression 的文字編輯器中 (如 EditPlus 等) ，我也能夠很容易地找到一些俱備相同規則的字串。所以學會 Regular Expression 是一個好的程式開發者所必定會經歷的過程。
但是 Regular Expression 是有名的難懂，而且通常寫完之後就很難再看懂它要表達什麼；所以這次介紹的這篇文章，就很清楚地告訴我們要如何寫出讓人能夠容易閱讀的 Regular Expression 。
廢話不多說了，請自行參考吧。 
文章網址：正則表達式的五個成功習慣 (簡體中文) 
重點提示： 
對於大部分程序員來說，在一個正則表達式環境裡使用空格和縮進排列都不成問題，如果他們沒有這麼做一定會被同行甚至外行人士看笑話。幾乎每個人都知道把代碼擠在一行會難於閱讀、書寫和維護。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>Regular Expression (正規表示式、常規表示式、正規表達式) 是很多程式開發者所懼怕的一種語法；但是因為它的功能強大，使得軟體界有個不成文的說法：沒學會 Regular Expression ，就不能稱為高手。</p>
<p>我是後來才學會 Regular Expression 怎麼寫的，不過我沒有因此而變成一個高手。但是 Regular Expression 讓我的程式功力大大的進步，而且在支援 Regular Expression 的文字編輯器中 (如 EditPlus 等) ，我也能夠很容易地找到一些俱備相同規則的字串。所以學會 Regular Expression 是一個好的程式開發者所必定會經歷的過程。</p>
<p>但是 Regular Expression 是有名的難懂，而且通常寫完之後就很難再看懂它要表達什麼；所以這次介紹的這篇文章，就很清楚地告訴我們要如何寫出讓人能夠容易閱讀的 Regular Expression 。</p>
<p>廢話不多說了，請自行參考吧。 </p>
<p>文章網址：<a href="http://www.phpv.net/article.php/1506">正則表達式的五個成功習慣</a> (簡體中文) </p>
<p>重點提示： </p>
<blockquote>對於大部分程序員來說，在一個正則表達式環境裡使用空格和縮進排列都不成問題，如果他們沒有這麼做一定會被同行甚至外行人士看笑話。幾乎每個人都知道把代碼擠在一行會難於閱讀、書寫和維護。</blockquote>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/1480444.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/1480444.html</guid>
	<category>程式開發</category>
	<pubDate>Wed, 26 Apr 2006 22:08:32 +0800</pubDate>
</item>
<item>
	<title>[好站] Ruby 教學</title>
	<description><![CDATA[
	關於 Ruby 的一些教學，因為國內還沒有什麼書可看，但是有些中文網站可以參考：

資源收集筒
Ruby台灣使用者社群
Ruby 學習筆記

有看到其他的好站時再補充。
不過個人對 Ruby 的語法實在是很難完全接受， PHP5 比較接近我的習慣。不過可惜 PHP5 沒有 RoR 這種殺手級應用，這點希望往後能有所突破。
以下連結由 CFC 提供，感謝他 ^^

Ruby &amp; Rails (一些有參考價值的連結) 
$Jslee_s-&gt;blog();
Aluminum Oxide

再補充：

do |x| end Ruby on Rails
深入Rails

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>關於 Ruby 的一些教學，因為國內還沒有什麼書可看，但是有些中文網站可以參考：</p>
<ul>
<li><a href="http://cfc.zuso.tw:3000/">資源收集筒</a></li>
<li><a href="http://www.ruby.oss.tw/html/">Ruby台灣使用者社群</a></li>
<li><a href="http://www.caterpillar.onlyfun.net/Gossip/RubyGossip/RubyGossip.html">Ruby 學習筆記</a></li>
</ul>
<p>有看到其他的好站時再補充。</p>
<p>不過個人對 Ruby 的語法實在是很難完全接受， PHP5 比較接近我的習慣。不過可惜 PHP5 沒有 RoR 這種殺手級應用，這點希望往後能有所突破。</p>
<p>以下連結由 CFC 提供，感謝他 ^^</p>
<ul>
<li><a href="http://anw.stikipad.com/ocean/show/HomePage">Ruby &amp; Rails (一些有參考價值的連結) </a></li>
<li><a href="http://jslee.name/">$Jslee_s-&gt;blog();</a></li>
<li><a href="http://pityathome.com/blog/">Aluminum Oxide</a></li>
</ul>
<p>再補充：</p>
<ul>
<li><a href="http://andrewhocoo.blogspot.com/">do |x| end Ruby on Rails</a></li>
<li><a href="http://willh.org/cfc/wiki/index.php/%E6%B7%B1%E5%85%A5Rails">深入Rails</a></li>
</ul>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/1433399.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/1433399.html</guid>
	<category>程式開發</category>
	<pubDate>Tue, 18 Apr 2006 14:22:39 +0800</pubDate>
</item>
<item>
	<title>鬼才 - 用文言文寫程式</title>
	<description><![CDATA[
	用中文寫 Perl
我只能說...真的是去看到鬼...
	]]>
	</description>
	<content:encoded><![CDATA[
	<p><a href="http://groups.google.com/group/tw.bbs.comp.lang.perl/browse_thread/thread/f0f95423c7cd71c5/bb1374f3f8cf10cf?tvc=2">用中文寫 Perl</a></p>
<p>我只能說...真的是去看到鬼...</p>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/1410915.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/1410915.html</guid>
	<category>程式開發</category>
	<pubDate>Thu, 13 Apr 2006 23:56:47 +0800</pubDate>
</item>
<item>
	<title>物件導向不僅如此</title>
	<description><![CDATA[
	我在藍色小鋪發表我的 ASP 物件導向心得之後，在矇矇的秘密基地裡也看到了一篇 &lt;不要從程式語言學習「物件導向」&gt; 。
其實這對我有很大的啟發，我以為我的文章已經描述了我在 ASP 上的物件導向思維，但是在我請教 Kenming Wang (就是 &lt;不要從程式語言學習「物件導向」&gt; 的作者) 後，他說了一句話：「您的 ASP 文章仍是藉由程式語言的層級來解釋物件的思維。」這對我簡直就是當頭棒喝！
我自己回頭看看我寫的東西，的確，我完全沒有提到我為什麼用這種方式來寫 ASP ，我只是賣弄我在 ASP 上所會的皮毛而已。看的人 (也就是大家) 一定還是不瞭解我所謂的物件導向思維到底是什麼。
雖然要承認錯誤很容易，但我想只這樣說說，完全是不負責任的作法。我的物件導向思維裡，容易擴充而易用性絕不能只是空口漫談而已，因此我想後面我會再補幾篇我精簡過的專案，用實際的例子來說明我這麼做的好處。
我想這邊要跟大家說聲抱歉，也感謝 Kenming Wang 的建言，他讓我上了寶貴的一課。
	]]>
	</description>
	<content:encoded><![CDATA[
	<p>我在<a href="http://www.blueshop.com.tw/">藍色小鋪</a>發表<a href="http://www.blueshop.com.tw/article/show.asp?cde=ATL200512062211496AK">我的 ASP 物件導向心得</a>之後，在<a href="http://www.kenming.idv.tw/">矇矇的秘密基地</a>裡也看到了一篇 &lt;<a href="http://www.kenming.idv.tw/index.php?title=ac_eb_af_cu_af_eo_eu_ascci_a_c_carpad_a&more=1&c=1&tb=1&pb=1">不要從程式語言學習「物件導向」</a>&gt; 。
<p>其實這對我有很大的啟發，我以為我的文章已經描述了我在 ASP 上的物件導向思維，但是在我請教 Kenming Wang (就是 &lt;不要從程式語言學習「物件導向」&gt; 的作者) 後，他說了一句話：「您的 ASP 文章仍是藉由程式語言的層級來解釋物件的思維。」這對我簡直就是當頭棒喝！</p>
<p>我自己回頭看看我寫的東西，的確，我完全沒有提到我為什麼用這種方式來寫 ASP ，我只是賣弄我在 ASP 上所會的皮毛而已。看的人 (也就是大家) 一定還是不瞭解我所謂的物件導向思維到底是什麼。</p>
<p>雖然要承認錯誤很容易，但我想只這樣說說，完全是不負責任的作法。我的物件導向思維裡，容易擴充而易用性絕不能只是空口漫談而已，因此我想後面我會再補幾篇我精簡過的專案，用實際的例子來說明我這麼做的好處。</p>
<p>我想這邊要跟大家說聲抱歉，也感謝 Kenming Wang 的建言，他讓我上了寶貴的一課。</p>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/1195928.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/1195928.html</guid>
	<category>程式開發</category>
	<pubDate>Thu, 02 Mar 2006 22:03:38 +0800</pubDate>
</item>
</channel>
</rss>