更新於 3 年前

設計模式學習筆記(一):前言

/Design-pattern/intro/cover.png

什麼是設計模式 (Design Pattern)

先說說什麼是模式(Pattern)? 一再重複出現的東西、事件、現象就稱為模式 ,舉例來說:
「怎麼每次進你房間你都在看著電腦桌布發呆?」媽媽不懂電腦桌布到底有什麼好看的。
「又發生神秘密室殺人事件!」走到哪都會發生案件,那個如同死神般的小學生。
「不出意外的話,要出意外了!」大家都有預感劇情差不多該開始走下坡了。
「XXX學習筆記」如本篇標題所示。
諸如此類,反覆出現的特性。

設計模式是指在軟體發展中,經過驗證的,用於解決在特定環境下、重複出現的、特定問題的解決方案。 --《王者歸來 品味 JAVA 的 21 種設計模式》

設計模式一詞起源於建築業,建築師克里斯托佛·亞歷山大 ( Christopher Alexander ) 在 1970 年代末編制了一本匯集設計模式的書,而後人們將其概念應用在軟體設計上。

1994 年,由於 Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 合著的《Design Patterns: Elements of Reusable Object-Oriented Software (設計模式 -- 可復用物件導向軟體的基礎)》一書的出版,正式掀開了軟體業設計模式的序幕,因為書名跟作者名稱都太長了,後來人們便簡稱 " GoF (Gang of Four 四人幫) 的書" 。

GoF所提出的設計模式總共有23種,根據情境分成三大類型,建立型 、結構型 、行為型 。

建立型模式(Creational Patterns)

建立型模式試圖根據適合的情況來決定建立物件。單純的物件創建常會導致一些設計問題或增加設計的複雜度。創建型模式抽象了物件產生實體的過程,用來說明建立物件的實例。

  • 簡單工廠(Simple Factory)
  • 工廠方法(Factory)
  • 抽象工廠(Abstract Factory)
  • 生成器(Builder)
  • 原型(Protoype)
  • 單例(Singleteon)

結構型模式 (Structural Patterns)

設計適用不同情境下的物件間關係結構,藉由一以貫之的方式來了解元件間的關係,以簡化設計。

  • 適配器(Adpater)
  • 橋接(Bridge)
  • 組合(ComPosite)
  • 裝飾者 (Decorater)
  • 外觀(Facade)
  • 享原(Flyweight)
  • 代理(Proxy)

行為型模式 (Behavioral Patterns)

物件之間的合作行為構成了程式最終的行為,物件之間若有設 計良好的行為互動,不僅使得程式執行時更有效率,更可以讓物件的職責更為清晰、整個程式的動態結構(像是物件調度)更有彈性

  • 責任鏈(Chain of Responsibility)
  • 命令(Command)
  • 迭代器 (Iterator)
  • 中介者(Mediator)
  • 備忘錄(Memento)
  • 觀察者(Observer)
  • 狀態(State)
  • 策略(Strategy)
  • 模板(Template Method)
  • 訪問者(Visitor)

我為什麼要學設計模式

可以讓你的程式看起來更牛逼,要求老闆加薪更有底氣

在軟體的世界裡「變動」才是常態,不易擴充將耗費更多的時間以同時滿足新、舊需求。我們的軟體就像是在變動中航行的船,變動是一波接一波撲面而來的浪,我們無法預測下一波的浪會有多大,能做的只有想辦法加固我們的船讓我們在風浪中能夠應變,繼續航行。

學習設計模式的理由有很多:

  • 每一種設計模式都遵守 物件導向設計原則
    • 設計模式是我們遵守物件導向設計原則後為了達成某個特定的軟體需求 , 所完成的實作方式。
  • 避免 重蹈前人覆轍 ,從他人經驗中獲益,每一種設計模式都可以 重複利用 ,不用再重複造輪子
    • 某些問題同質性非常高 , 它經常會出現在不同的軟體實做中。而設計模式就是針對這些一再出現的問題所歸納出的解決方法,讓人們可將時間花在其他更需要解決的問題上。
  • 確立通用的術語 ,設計模式定義了一種讓你和團隊成員能夠更高效溝通的通用語言
    • 你只需說 “哦,這裡用單例就可以了”,所有人都會理解這條建議背後的想法。只要知曉模式及其名稱,你就無需再解釋什麼是單例了。
  • 提供了觀察問題、設計過程和物件導向的更高層次的視角,使我們不會落入「 過早實作細節 」的陷阱裡
    • 設計模式通常會針對問題的核心點分析 , 並且經過一體化的思考以及歸納 , 達到讓程式可以適應更多的變動,並使你學會解決更多相似問題的方法。更多設計模式是應用在 Libraries 上,學會設計模式可讓你更加了解你所用的 Libraries 是如何運作的。
  • 是提升個人 技術能力的捷徑
    • 學習設計模式,可以學習到前輩們的經驗,吸收和領會其設計思想,掌握他們解決問題的方法,就好像站在這些巨人的肩膀上,讓我們個人的技術能力得到快速的提升。

正所謂有人的地方就有江湖,有變更的地方就會有 bug。

但,設計模式真有這麼神奇?

說說爭議

設計模式自其誕生之就飽受爭議,例如,《Coders at work》中首位受訪者 Jamie Zawinski 毫不客氣地談到:「我認為這本書是 一派胡言 (was crap),給人的感覺好像程式設計只要剪貼就能搞定」。

Ruby語言創建者在《松本行弘的程式世界》中,也談到他對設計模式該書的第一印象:「造成這麼大話題的,居然是這麼 理所當然的東西 」、「有些模式在 C++Java 這類靜態定型語言中很有效,但在 SmalltalkRuby 這類動態定型語言之中,並沒有多大意義」。

為什麼會有這麼多批評?因為在有經驗的程式設計師眼裡,設計模式往往不會是最重要的,相反的,會寧願新手先把程式碼寫好,也不要先去學設計模式。當你先去學設計模式,你可能會硬套進現有專案,變成有設計模式的糙 code,相反的,如果你先把程式寫好,你可能在不知不覺中就已經會使用幾種設計模式了。

而若沒有仔細去考量待解的問題,只想找出幾個相近的模式直接套用,那設計模式這本書就成了Jamie Zawinski 說的:「那根本就不是程式設計,而是著色書了」。

如果你手上只有一把槌子,那麼任何東西看上去都像是釘子。

設計模式反映的是過去程式語言的缺陷

Peter Norvig在1996年的〈Design Patterns in Dynamic Programming〉演講中,指出:「設計模式在某種意義上,是為了解決物件導向語言本身缺陷的權宜之計」。

以現代的眼光來看某些設計模式可能會覺得沒有必要或是過度設計,但我們應該要知道,GoF 的書是在 1994 年出版的書籍,書中的範例主要使用的語言是 C++ ,部分段落使用 Smalltalk 。那時 Java、C# 都還沒誕生呢 ( Java 誕生於1995年5月23日、 C#  誕生於2000年),所以肯定會有很多語言上的限制,導致開發時必須要使用特定的方法來避開問題。

然而隨著時代演進,部分模式的概念、實作已漸漸內化為語言的特性,當人們需要使用相關模式時,早已不用自己製作輪子,直接使用內建的函式或類別即可。最經典的便是迭代器模式, Java 已經有相似概念的內建類別( CollectionIterator ),又或是策略模式在 Java 中可以簡單地使用匿名(lambda)函數來實現。

如何學習設計模式

在開始學習設計模式以前,首先,你應該要熟悉 物件導向 的概念 ( 介面、類別、物件、封裝、繼承、多型 ) 以及設計模式的 五項基本原則 SOLID ,還有學會閱讀 UML (類別圖) 描述的類別/物件之間的關係。最後再嘗試 寫出一個專案 就可以開始學設計模式了。

因為設計模式基本上只是五項原則的延伸,不用非常精通 SOLID 才能學設計模式,稍微理解 SOLID 原則就可以開始,重點是要一邊學設計模式,一邊從設計模式中印證和加深自己對 SOLID 的理解。

設計模式是從軟體實作中演化而來,被經驗豐富的程式設計師整理歸納出來,最後變成設計模式的相關書籍。所以,真正要練習設計模式,就應該 親手練習撰寫大量代碼 ,練習設計,並透過使用案例做調整,練習 重構重構再重構 ,並在實務中找到合適的設計模式做應用。

實作永遠是最好的學習模式。 當你有一個完整專案就可以嘗試用設計模式去重構你的程式碼。但重構的時候要知道為什麼要用、思考用了模式跟不用模式的差別是什麼,檢視是否合適,這樣才能把學習效果最大化。

重構 (Refactoring),隨時保持要 接受重構 的心。

結尾

沒有最好的設計模式,只有最適合的設計模式,而這也正是學習設計模式最困難的地方

  • 如果說 SOLID 設計原則是我們設計程式時應該遵守的原則或是心法 , 那 設計模式就是 SOP 模板 , 只要按照步驟執行 , 就可以輕易地達成 SOLID 原則對於程式的要求。
  • 設計模式 只是一種解決問題的方法 , 這代表我們在解決自己的問題時 , 並不需要完全按照設計模式的結構 , 可以適時地加入自己的變化。
  • 設計模式只是提出一個規範和定義 , 並 不代表是唯一的解決方法 。因此當我們針對某個設計模式進行修改/變形 , 只要其符合 GOF 所表達的情境 , 就可以說是實作 XXX 模式了。
  • 使用模式最恰當的時機是 有變更的需求的時候 對代碼進行重構,並且注意,一次重構只使用最適當的設計模式,能解決本次變更的需求即可。
  • 不要僵化地使用設計模式固有的解法,而是 理解需求 ,在使用設計模式時,一併考慮程式語言的特性因事制宜。
  • 要知道 使用設計模式是需要付出代價的 ,例如:複雜度、效率、開發時程。也許對開始的開發時期沒有幫助,但換來的通常是變更、維護時期的好處,例如:易修改、較好的擴充性、可讀性。

後記

Java 界的大家長,林信良前輩突然就過世了,在找資料的時候常常會翻到這位前輩的文章。願您一路好走,R.I.P。

參考資料

設計模式不死? | iThome 常用设计模式有哪些? (refactoringguru.cn) DAY5: 簡單介紹設計模式 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw) 我能學 design pattern 嗎? - HackMD Day 03: 面對 Design Pattern 該有的知識 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw)