跳到主要內容

發表文章

整理設計模式

        依據 GOF 的書,可以將經典的設計模式分為以下三類:生成、行為、結構。 生成模式 :牽涉到 將物件實體化 。這類模式都提供一個方法,將客戶從所需要實體化的物件中鬆綁出來。 獨體模式 (Singleton Pattern) 工廠方法模式 (Factory Method Pattern) 抽象工廠模式 (Abstract Factory Pattern) 建立者模式 (Builder Pattern) 原型模式 (Prototype Pattern) 結構模式 :讓你 合成類別或物件到大型的結構 。 裝飾者模式 (Decorator Pattern) 轉接器模式 (Adapter Pattern) 表象模式 (Facade Pattern) 合成模式 (Composite Pattern) 代理人模式 (Proxy Pattern) 橋接模式 (Bridge Pattern) 享元模式 (Flyweight Pattern) 行為模式 :模述 類別和物件如何互動 ,以及 各自的責任 。 策略模式 (Strategy Pattern) 觀察者模式 (Observer Pattern) 命令模式 (Command Pattern) 樣板方法模式 (Template Method Pattern) 反覆器模式 (Iterator Pattern) 狀態模式 (State Pattern) 責任鏈模式 (Chain of Responsibility Pattern) 解譯器模式 (Interpreter Pattern) 中介者模式 (Mediator Pattern) 備忘錄模式 (Memento Pattern) 訪問者模式 (Visitor Pattern)         有人可能會覺得裝飾者模式明明有替物件增加行為,為什麼不算是行為模式呢?我們可以從上面的結構模式得知, 結構模式用來描述類別或物件如何被合成,以建立新的結構或功能 。裝飾者模式允許你透過「 將某物件包裝進另一個物件的方式 」,將物件合成以提供新功能,因此焦點應該放在「 動態合成物件,以取得某功能 」,而不是物件之間的溝通。         設入淺出設計模式也有提到一些使用設計模式的
最近的文章

訪問者模式 (Visitor Pattern)

        假設你設計一個系統,其中會有一些相似類別,類別中都有某些方法內容相似,但還是需要判斷目前要做事的是哪個類別才能呼叫對應的適當類別。通常遇到這種情情,在 Java 中最直接的做法就是使用 instanceof 關鍵字來判斷,如以下的簡單範例: public interface CarComponent { public void printMessage(); } public class Wheel implements CarComponent { @Override public void printMessage() { System.out.println("This is a wheel"); } // 這是 Wheel 跟 Engine 不同的方法 public void doWheel() { System.out.println("Checking wheel..."); } } public class Engine implements CarComponent { @Override public void printMessage() { System.out.println("This is a engine"); } // 這是 Wheel 跟 Engine 不同的方法 public void doEngine() { System.out.println("Testing this engine..."); } } public class Car { private List mComponents; public Car() { mComponents = new ArrayList<carcomponent>(); } // 有些時候我們還是需要針對不同類別去做不同的事情 public void setComponent(CarCompon

原型模式 (Prototype Pattern)

        原型模式,光看字面上的意思很難了解這在幹麻,我們直接先來看正式定義及類別圖: (中文好難翻譯,我不會…) Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.         從定義上我們大概可以知道,這個模式的用途是讓我們可以 透過複製的方式產生一個新物件 。可是產生物件幹麻要這麼麻煩,不是直接 new 出一個新物件就好了嗎? 還是一句老話: 有時建立物件的代價很高 。假如你所需要建立的物件,其所需的資料是放在遠端資料庫的,這樣當每建立一個物件,就要讀取資料庫一次,這其實是很花成本的。         從類別圖我們可以看到,有一個公開的介面 Prototype,其子類別都需要實作 clone。而 client 這邊在取得物件時,不是透過 new 的方式,而是使用 clone() 來取得所需的物件。因此我們可以知道,此模式的重點在於 client 的程式碼可以在不知道特定類別為何的情形下可以生成新的物件 。         接下來來看簡單的範例程式碼吧。在 Java 中,每個類別都繼承了 Object 這個類別,表示每個類別都含有 clone() 這個方法。但是要讓 clone() 能動,類別還必須實作 java.lang.Cloneable 這個標籤介面( Tag Interface,Marker Interface )。除了這點之外,還要注意你的 clone() 是想要淺層複製 (shallow copy) 還是深層複製 (deep copy)。 // 在 Java, 要實作 Cloneable // 才能 override Object 的 clone() 方法 public class SomeObj implements Cloneable { // 某個 primitive type 成員, // shallow copy 可以複製值 private int mNum; // 某個類別物件, // shallow copy 只會複製 reference, private Ob

備忘錄模式 (Memento Pattern)

        備忘錄模式的用途很單純,就是 提供物件回到之前狀態的功能,簡單說就是備份 (存檔) 的機制 。備忘錄模式是一個在現實世界中很常使用到的模式,如遊戲的儲存記錄,文書編輯器的「上一步」功能等。簡單介紹完這個模式後,就來看一下正式的定義及類別圖吧: 不違反封裝的情形下,取得物件的內部狀態。如此可以回復物件之前的狀態。 Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. Originator:就是定義中提到的要保留內部狀態的物件。現實例子就像是遊戲角色狀態,或是文書編輯器中的文字等。 Memento:保留 Originator 內部狀態 (資料) 的物件,例如遊戲中要存檔的資料。 Caretaker:主要功用是管理 Memento 物件。         看上面的介紹,可能有人會有疑惑,為什麼不要 Originator 自己處理存檔功能就好,還要花心力額外獨立出存檔跟管理存檔的物件?這樣做的目的其實是為了讓 類別的權責單一化(單一責任守則:一個類別應該只有一個改變的理由。) 。以生活中的例子來說,不把遊戲記錄檔獨立出來,你要怎麼拿到你朋友的超強記錄呢XD 而且 Caretaker 能管理的不是只有一個 Memento 物件,就好比文書編輯器通常不可能只「上一步」一次,要是把處理多個狀態的功能,以及保留目前狀態的功能,都放在 Originator,Originator 功能會太複雜,未來也不好維護。         看到這邊,我們應該能知道備忘錄模式有兩個目標: 儲存物件的重要狀態 維護物件的封裝         接下來來看簡單的程式碼來看備忘錄模式是如何運作的: // Originator public class GamePlayer { // 遊戲角色的生命值 private int mHp; // 遊戲角色的經驗值 private int mExp; public GamePlayer(int hp, int e

中介者模式 (Mediator Pattern)

        中介者模式,又稱栛調者模式,直接照字面上解釋,就是有一個中介者負責處理事情。因為有一個 中介者可以負責事情,如處理、傳遞、通知,就可以簡化物件之間的溝通和控制制方式,進而降低物件之間的耦合性 (相依性,依賴性)。 中介者模式定義一個可以封裝一組物件互動的物件,可以使物件不用直接互相引用而降低耦合性,且可以獨立改變物件之間的互動關係。 Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.         類別圖中的 Colleague 相關類別,指的就是實際上要做事,彼此可能因為不同的需求而會有相依性的類別。ConcreteColleague 之間不會直接溝通,而是透過 Mediator。看到這邊,可以順便跟 表象模式 來做個比較。這兩個模式都同樣有管理一群類別的味道,但不同之處是在於 Client 的角度。表象模式中,Client 是不會直接跟物件們溝通,都是透過表象來做事。而中介者模式中,Client 還是可以直接跟物件們溝通,只不過 中介者模式是強調的是物件們不會直接溝通 。另外表象模式跟中介者模式都是很好的 認識極少化守則(又稱 迪米特法則 )實踐 ,表象模式是把這個守則應用在 Client 跟要溝通的物件們,而中介者模式是把這個守則應用在物件們之間的溝通。         接下來看個簡單的範例吧。假設你現在要設計一個 GUI 的登入畫面,有四個元件:可以讓使用者輸入帳號密碼的 EditText,顯示帳號或是密碼有問題的 TextView,以及可以讓使用者確定要登入的 Button。一般來說,當帳號密碼有問題,登入 Button 就不能按,且 TextView 可能需要顯示提示訊息。當沒有使用中介者模式時,這些元件可能彼此都要互相知道才能達到上述需求,但加入了中介者模式後: // Colleague 介面 public abstract

解譯器模式 (Interpreter Pattern)

        解譯器模式簡單來說就是 把一句有特殊規則的語句,透過解釋器將它真正的意思表現出來 。相信有學過 Context-free grammar (CFG),Backus–Naur form (BNF),或是 Compiler 相關程的人會比較了解這個模式 (不是我,我早就忘光了…)。不知道上面術語的人,還是可以透過接下來的介紹來稍微了解這個模式。先來看一下這個模式的正式定義及類別圖: 定義一個語言與其文法,使用一個解譯器來表示這個語言的敘述 Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language Context 通常是指待解譯的語句 AbstractExpression 是所有規則都要實作的介面 TerminalExpression 是指無法再展開的規則,算是最小單位的規則 NonterminalExpression 是指可以再展開的規則,可以展開成 NonterninalExpression 和 TerminalExpression 的組合         從類別圖可以看到,語法可能可以一直展開,這時就可以用語法樹來表示,而語法樹以程式來表達的話,就可以使用 合成模式 。而通常比較正式的語法會用 BNF 來表示,如下: expression ::= plus | minus | variable | number plus ::= expression expression '+' minus ::= expression expression '-' variable ::= 'a' | 'b' | 'c' | ... | 'z' digit = '0' | '1' | ... | '9' number ::= digit | digit number 而 每個語法都會定義一個類別 ,如 pl

享元模式 (Flyweight Pattern)

        享元模式,又稱蠅量級模式 (Flyweight 是拳擊術語,指最輕量的級別,蠅量級),單看文字可能有點難理解,但簡單來說就是 共享元件(資源) 。試想一個情節:公司裡每個人都會有員工資訊,也都會有名片。假設名片是個類別時,對於每個員工,都需要 new 出名片的實體,而實際上名片裡也只是內容不同而已。當 員工越多時,就需要 new 出越多名片物件,造成記憶體浪費 ,這時候就適合使用享元模式。以下為享元模式的正式定義及類別圖: 共享物件,用來儘可能減少記憶體使用量以及分享資訊給儘可能多的相似物件。 Use sharing to support large numbers of fine-grained objects efficiently. Flyweight:所有具象 Flyweight 子類別的共同介面 ConcreteFlyweight:Flyweight 的具象子類別,就是要共享的元件類別。共享元件本身有內部的資料或狀態的話,都是在這裡維護。 FlyweightFactory:負責創建與管理共享元件 (如例子中的名片)。通常會確認共享元件是否已存在,存在就直接給 Client 用,否則就創建新的共享元件。 Client:取得共享元件並使用的角色,通常會結合外部資訊來使用共享元件 (如例子中的員工資訊) // 名片是共享元件 // 提供一個介面讓子類別實作 public abstract class NameCard { // 名片要顯示的資訊 protected String mId; protected String mDept; protected String mTel; public abstract void show(Employee e); } public class GeneralNameCard extends NameCard { @Override public void show(Employee e) { // 從外部資源取得資料 mId = e.getId(); mDept = e.getDept(); mTel = e.getTel();