大衛總是因爲不同女友而要表現的行爲都有所不同,而搞得自己暈頭轉向,但後來學習了 Strategy 設計模式後,他就更有組織地管理他的女友們了,女友們也變得更滿意了,一起來看看他怎麼做到的吧!
HIHI~😍 如果你是第一次來的話,『Chan-Chan-Dev』是一個專門用簡單的圖文與故事講解網路程式技術的網站。 若你也喜歡用這種方式學習的話,歡迎加入 Chan-Chan-Dev Facebook 的粉絲團,在發佈的時候就有比較多機會收到通知喔!😍
在寫程式的過程中,一定很常遇到需要很多不同情況而有不同行爲(策略)的功能。
這樣子例子在生活中其實滿常見的,例如天氣變冷了,我們受到 Natural call 就會不自覺地想要吃火鍋,而天氣變熱了,我們就會覺得吃涼麵比較清爽,明明都是『吃』的行爲,但是卻因爲『氣溫』的情況不同,就會讓這個『吃』的行爲(策略)而有所不同。
所以如果要達到上述的不同情況有不同的行爲,那條件語句是邏輯判斷的過程中無可避免的必須,但隨著情況增長,條件語句也會跟着變多,很快地就會難以維護了。
因此很多不同的情況下,要如何用更少的 if else 更有結構與更有效率地來完成一樣的功能呢?
Strategy 設計模式提供了一套棒的解法,讓我們用個小故事來輕鬆地學習如何將 Strategy 設計模式運用在我們日常的開發當中吧 😍
Strategy 設計模式是什麼? Strategy 是一種行爲類的設計模式,讓一個物件因爲情況的改變,進而改變行爲(策略)。
如果沒用 Strategy 會怎樣嗎?🧐 讓我們一樣用一個故事來當起手式,這次要講講花心劈腿男大衛的故事,大衛是個花心的劈腿渣男,但是他卻很得意的自喻爲自己是 2020 年的現代韋小寶,他的夢想就是備齊他的十二金釵,過著十二女友同時在線且人人稱羨的幸福快樂的日子。只是他距離這個夢想還有點遙遠,因爲他目前只交到到三個女朋友,分別是:Mary、Babe、Linda。
三個女朋友各有自己興趣、喜歡吃的東西、親密的暱稱,以及他騙那些女朋友他目前自己的職業是什麼,以下簡單地圖解整理一下:
所以每次大衛要跟他們出去約會的時候,就要記得他女朋友的每個資訊細節,而且需要針對不同的女友(策略)表現出正確的行爲。如果不小心把 Mary 叫成北鼻,或者是跟 Linda 說,我這個禮拜要在營區留守的話,他的劈腿大計就會馬上就會識被了。所以他要針對這些不同女朋友(策略)而去改變他的稱呼方法(行爲)。
所以在還沒學過 Strategy 設計模式的大衛會怎麼管理他的女友們呢?
David Class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class David { private String girlFriend; public David (String girlFriend) { System.out.println("目前在陪的女朋友:" + girlFriend); this .girlFriend = girlFriend; } public void changeGirlFriend (String girlFriend) { System.out.println("-----------------------" ); System.out.println("目前在陪的女朋友:" + girlFriend); this .girlFriend = girlFriend; } public void askWhatToEet () { if (girlFriend.equals("Mary" )) { System.out.println("要不要去吃韓式烤肉?" ); } if (girlFriend.equals("Babe" )) { System.out.println("要不要去吃漢堡王?" ); } if (girlFriend.equals("Linda" )) { System.out.println("要不要去吃法國菜?" ); } } public void askWantToGo () { if (girlFriend.equals("Mary" )) { System.out.println("我這邊有多一張免費的電影票,要不要去看電影呀?" ); } if (girlFriend.equals("Babe" )) { System.out.println("最近好像有新開一個 Outlet,要不要去看看?" ); } if (girlFriend.equals("Linda" )) { System.out.println("我們要不要去誠品看看有什麼新書嗎?" ); } } public void sayHello () { if (girlFriend.equals("Mary" )) { System.out.println("豬豬,想不想我?" ); } if (girlFriend.equals("Babe" )) { System.out.println("我好愛你喔,北鼻 😍" ); } if (girlFriend.equals("Linda" )) { System.out.println("親愛的,你今天想我了嗎?" ); } } public void findExcuseToEscape () { if (girlFriend.equals("Mary" )) { System.out.println("豬豬,我這個週末被連長留下來留守,所以不能陪你了,哭哭。" ); } if (girlFriend.equals("Babe" )) { System.out.println("北鼻,這個禮拜老闆剛好派我到地中海去出差,所以可能這週無法陪你,要想我喔。" ); } if (girlFriend.equals("Linda" )) { System.out.println("親愛的,這個禮拜要陪教授開研討會,你要自己堅強喔。" ); } } }
Main Class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Main { public static void main (String[] args) { David david = new David ("Mary" ); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); david.changeGirlFriend("Babe" ); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); david.changeGirlFriend("Linda" ); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); } }
而最近 David 又與他的夢想拉近一點距離了,因爲他在酒吧成功地把到一位新的混血女友 June,而她的資訊如下:
所以就會需要在每一個不同的行爲內在新增新的女友 June 的判斷條件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public void askWhatToEet () { if (girlFriend.equals("June" )) { System.out.println("要不要去逛饒河夜市" ); } } public void askWantToGo () { if (girlFriend.equals("June" )) { System.out.println("我們今天要來看 Netflix 上有什麼好看的嗎?" ); } } public void sayHello () { if (girlFriend.equals("June" )) { System.out.println("哈尼,我不能沒有你!" ); } } public void findExcuseToEscape () { if (girlFriend.equals("June" )) { System.out.println("哈尼,醫院要值班留守,所以可能無法陪你喔" ); } }
問題:判斷式會因爲不同的情況增加 😭 由 David class 可以看出所有的要行爲都需要判斷目前的女友是哪一位,然後進行在這個女友的情況下的合理行爲,所以隨著 David 的女朋友變多,每一個行為的判斷式就會越多,就像是新增 June 一樣需要在每一個行爲新增判斷式。所以可想而知的是:
有 12 個女友可能就會需要 12 個判斷式來符合把每個女朋友安撫地服服貼貼的需求。在這種架構之下,很難讓新增女友這件事情可以被規模化! 😆
所以 David 去上了女友管理學線上課程,學習到了 Strategy 的設計模式,以下是他學習後的筆記內容。
1. 定義在不同情況下執行的行爲(策略)界面 1 2 3 4 5 6 7 8 9 10 11 12 public interface IGirlFriend { public void askWhatToEet () ; public void askWantToGo () ; public void sayHello () ; public void findExcuseToEscape () ; public String showName () ; }
2. 將每個情況(女友)抽成一個獨立的 class ,並且實作上述不同情況下執行的行爲(策略)界面 所以我們會得到以下的女友(策略) class
Mary class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Mary implements IGirlFriend { @Override public void askWhatToEet () { System.out.println("要不要去吃韓式烤肉?" ); } @Override public void askWantToGo () { System.out.println("我這邊有多一張免費的電影票,要不要去看電影呀?" ); } @Override public void sayHello () { System.out.println("豬豬,想不想我?" ); } @Override public void findExcuseToEscape () { System.out.println("豬豬,我這個週末被連長留下來留守,所以不能陪你了,哭哭。" ); } @Override public String showName () { return "Mary" ; } }
Babe class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Babe implements IGirlFriend { @Override public void askWhatToEet () { System.out.println("要不要去吃漢堡王?" ); } @Override public void askWantToGo () { System.out.println("最近好像有新開一個 Outlet,要不要去看看?" ); } @Override public void sayHello () { System.out.println("我好愛你喔,北鼻 😍" ); } @Override public void findExcuseToEscape () { System.out.println("北鼻,這個禮拜老闆剛好派我到地中海去出差,所以可能這週無法陪你,要想我喔。" ); } @Override public String showName () { return "Babe" ; } }
Linda class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Linda implements IGirlFriend { @Override public void askWhatToEet () { System.out.println("要不要去吃法國菜?" ); } @Override public void askWantToGo () { System.out.println("我們要不要去誠品看看有什麼新書嗎?" ); } @Override public void sayHello () { System.out.println("親愛的,你今天想我了嗎?" ); } @Override public void findExcuseToEscape () { System.out.println("親愛的,這個禮拜要陪教授開研討會,你要自己堅強喔。" ); } @Override public String showName () { return "Linda" ; } }
經過以上調整後的 class,可以更一眼看到在這個女友之下,會需要做的行爲分別是什麼。
以單一職責的角度來說,這個 class 也專門負責這個女友的所有行爲,而不需要去管其他女友(其他策略)發生什麼事情。
如果對於單一職責的概念不太熟悉的朋友,也可以參考這篇 Single Responsibility Principle(單一職責) 快速地簡單瞭解一下喔 😊。
3. 在 David 中將 girlFriend 屬性變成一種女友(策略)物件參考 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class David { private IGirlFriend girlFriend; public David (IGirlFriend girlFriend) { System.out.println("目前在陪的女朋友:" + girlFriend.showName()); this .girlFriend = girlFriend; } public void changeGirlFriend (IGirlFriend girlFriend) { System.out.println("-----------------------" ); System.out.println("目前在陪的女朋友:" + girlFriend.showName()); this .girlFriend = girlFriend; } }
4. 在 David 的所有行爲,都直接呼叫女友(策略)物件定義的方法即可 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void askWhatToEet () { girlFriend.askWhatToEet(); } public void askWantToGo () { girlFriend.askWantToGo(); } public void sayHello () { girlFriend.sayHello(); } public void findExcuseToEscape () { girlFriend.findExcuseToEscape(); }
Main Class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Main { public static void main (String[] args) { David david = new David (new Mary ()); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); david.changeGirlFriend(new Babe ()); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); david.changeGirlFriend(new Linda ()); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); } }
David class 調整過後,Main Class 調整的幅度不大,只是將這些策略從字串改爲物件的方式使用,但是可以從調整過後的 David class 看到,藉由 Strategy 的方式已經成功地將 David class 內需要針對每個女友(策略)所設定的條件判斷全部移除,而只是單純地呼叫女友(策略)物件內所定義的方法而已。
所以如果我們今天一樣多了一個新的混血女友 June 的話,我們會怎麼增加呢?我們只需要新增一個 June class,而 David class 根本不需要做任何的調整,我們就可以在 Main class 開心的使用 June 的女友(策略)物件囉!
June class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class June implements IGirlFriend { @Override public void askWhatToEet () { System.out.println("要不要去逛饒河夜市" ); } @Override public void askWantToGo () { System.out.println("我們今天要來看 Netflix 上有什麼好看得嗎?" ); } @Override public void sayHello () { System.out.println("哈尼,我不能沒有你!" ); } @Override public void findExcuseToEscape () { System.out.println("哈尼,醫院要值班留守,所以可能無法陪你喔" ); } @Override public String showName () { return "June" ; } }
Main class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Main { public static void main (String[] args) { david.changeGirlFriend(new June ()); david.sayHello(); david.askWantToGo(); david.askWhatToEet(); david.findExcuseToEscape(); } }
最後來看看調整之後的架構狀態 😊
這樣做是不是要擴充其他女友更爲方便了呢? 自從 David 學會了 Strategy 管理大法之後,他也更容易少發生不小心叫錯名字,或者是搞錯女朋友興趣的出包狀況,每個女朋友對他也更滿意了,也介紹了更多身邊的好閨蜜給 David 當女朋友,讓 David 很快就達到他的夢想了,甚至過沒多久就超越他的夢想了,一切都是 Strategy 的功勞呀!(這到底是什麼神展開 🤣)
所以男孩們!也夢想像人生勝利組 David 一樣管理好自己的女友們(策略們)嗎?那 Strategy 設計模式將會是你人生的必修課囉!
如果有任何問題都歡迎在底下留言,或者是哪裡有不小心講錯的地方也歡迎一起討論 ,最後感謝你收看到這裏囉 😍