跳到主要內容

發表文章

目前顯示的是 7月, 2016的文章

反覆器模式 (Iterator Pattern)

        假如你有一間早餐店,菜單用 ArrayList 存,有一間午餐店,菜單用 Array 存。今天你想把兩間餐廳合併成一間,但是馬上就遇到問題:要顯示菜單給顧客看時,要分別使用 for 來走訪 ArrayList 跟 Array,這樣造成程式維護上的困難,因為要寫兩次 for,未來要是合併另一間餐廳,又使用不同型態存菜單的話,程式碼不就又要大改? 是否有方法可以 封裝重複的動作呢 ? 答案是可以的喔,讓我們直接看程式碼來了解怎麼做。 // 這個就是菜單的共通介面 // 為了能夠走訪所有菜色 public interface Iterator { // 得知是否還有更多元素 boolean hasNext(); // 取得下一個元素 Object next(); } // 只是一個簡單的介面 // 可以取得一個反覆器, 取得菜單內容 public interface Menu { Iterator createIterator(); } // 以午餐店, 使用 Array 當例子 public class LunchMenuIterator implements Iterator { private MunuItem[] mItems; private int mPosition = 0; // 紀錄目前走訪的位置 // 建構式傳入一個菜單 Array public LunchMenuIterator(MenuItem[] items) { mItems = items; } @Override public Object next() { // 傳回目前位置的元素, 並增加位置 MenuItem menuItem = mItems[position]; position = position + 1; return menuItem; } @Override public boolean hasNext() { // 因為是 Array, 要檢查是否超出長度 if(position

樣板方法模式 (Template Method Pattern)

        你有一家飲料店,主要提供咖啡跟茶。泡咖啡跟泡茶的程式碼如以下所示: public class Coffee { // 這是店裡統一的泡咖啡SOP void prepareRecipe() { // 煮開水 boilWater(); // 用沸水沖泡咖啡 brewCoffeeGrinds(); // 把咖啡倒進杯子 pourInCup(); // 加糖和牛奶 addSugarAndMilk(); } // 底下省略這些方法的實作... } public class Tea { // 這是店裡統一的泡茶SOP void prepareRecipe() { // 煮開水 boilWater(); // 用沸水沖泡茶包 steepTeaBag(); // 把茶倒進杯子 pourInCup(); // 加檸檬 addSLemon(); } // 底下省略這些方法的實作... }         prepareRecipe看起來好像喔,應該可以抽象化。第一步很自然的把一些一樣的方法,如biolWater 跟 pourInCup,放到超類別裡,就稱為 Beverage。而雖然咖啡是沖泡「咖啡」、加糖跟牛奶,茶是沖泡「茶葉」、加檸檬,兩種飲料加的東西不一樣,但是做的「動作」是一樣的,只是處裡不同的原料而已。有了這些想法後,就可以來改上面的程式碼了: public abstract class Beverage { // 宣告為 final 是因為不想次類別 // 推翻這個方法, 這是統一的演算法 final void prepareRecipe() { // 煮開水 boilWater(); // 用沸水沖泡 // 跟上面程式碼比, 方法名更通用 brew(); // 把飲料倒

表象模式 (Facade Pattern)

        某天你心血來潮,想在家裡準備家庭劇院組。你為此做了一番研究,找來了覺得適合的播放器、投影機、螢幕、音響等等的設備,當你要準備開始看電影時,你要做哪些事呢? 將燈光調暗 開啟螢幕 打開投影機 設定投影機輸入模式 打開音響 設定音響音量 打開播放器 開始播放         將這些動作寫成程式碼的話: // 將燈光調暗到 10% 的亮度 lights.dim(10); screen.on(); projecter.on(); projecter.setInput(player); // 開啟喇叭 amp.on(); amp.setVolumn(5); player.on(); player.play();         這樣看起來做的事好像沒很多,但是當看完電影後,要把所有設備關掉要怎麼做?全部反向做一次嗎?假如要聽音樂而已,也要這麼麻煩嗎?未來升級新設備時,還要重新學習操作流程嗎?         這時候最直覺的想法一定是:把這些事包成一個 function 就好啦。這就是表象模式的精神所在,將一個或數個類別複雜的一切都隱藏起來,只露出美好的表面(就是簡化介面啦)。         使用表象模式,可以將一個複雜的次系統,變得容易使用。表象類別提供更合理的介面,來簡化原先的複雜介面。假如不想用表象介面時,還是可以直接操作次系統。         在這邊特別說明一下,表象模式跟 轉接器模式 雖然都是用來封裝類別,但是他們的目的卻是不同的。轉接器模式的目的是 改變介面以符合客戶的期望 。而表象模式是 提供次系統一個簡化的介面 。 public class HomeTheaterFacade { // 這就是合成 // 會用到的次系統元件全都在這裡 Amplifier mAmp; Player mPlayer; Projector mProjector; TheaterLights mLights; Screen mScreen; // 透過建構式將次系統元件記錄下來 public HomeTheaterFacade(Amplifier amp, Player pl

轉接器模式 (Adapter Pattern)

        此模式從字面上來看應該不難理解,用現實生活中的例子來看,就好比出國時想讓自己帶的電器也能在國外用,但國外的插座孔跟電器插頭不同時要怎麼辦?有一個 轉接頭 就可以啦~         以程式面來看,假如你有一個系統,希望它能和一家新廠商的 library 搭配使用,但新廠商的介面卻不同於舊廠商的介面。當不能或不想修改現有的程式碼時,就可以寫一個類別,將新廠商的介面轉換成你所希望的介面。         接下來看點程式碼吧。這裡使用 策略模式 中使用的鴨子類別,並新增一組火雞類別: // 鴨子介面 public interface Duck { public void quack(); public void fly(); } // 綠頭鴨是鴨子的次類別 public class MallardDuck implements Duck { @Override public void quack() { System.out.println("Quack"); } @Override public void fly() { System.out.println("I'm flying"); } } // 火雞介面 public interface Turkey { // 火雞只會喀喀叫 public void gobble(); public void fly(); } public class WildTurkey implements Turkey { @Override public void gobble() { System.out.println("Gobble gobble"); } @Override public void fly() { System.out.println("I'm flying a short distance"); } }         當你想用火雞物件來