在Gof書中寫到,設計模式是在特定情境中,為解決通用設計問題的自定義物件互動說明。許多開發者將焦點放在解決,模式名稱成了解決方案的代名詞,甚至將說明用的具體程式實際當成複製、修改的對象,忽略了設計模式實際上反映語言面對領域需求時的常見問題,這些問題在解決時出現類似的思考流程,而設計模式是將思考流程抽象化後進行分類,每個分類都代表了問題的抽象集合。

瞭解設計模式是鑑古知今
儘管實際面對的程式需求各不相同,然而多數問題存在某些重複的相似性,設計模式是思考問題相似性的過程,將觀察到的相似性從實際問題中抽取出來,討論設計上如何解決抽取出的相似性,以求面對類似的具體問題時,可以有現成的抽象概念可以套用,以降低思考過程的複雜度。

舉例來說,圖形介面開發者在面對使用者版面配置需求時,曾思索物件間如何組合,以提供自由組合版面的彈性,動畫剪輯開發者在面對使用者圖片、影像剪接需求時,曾思索物件間如何組合以提供自由組合影片的彈性,測試框架開發者也曾思索物件間如何組合,以提供自由組合測試案例的彈性。

以上問題的相似性是「自由組合」,解決方案也呈現類似結構與行為,因而抽取出組合(Composite)模式的概念,日後若觀察到問題有自由組合的需求,組合模式就可提供現成抽象概念,免去探索解決方案的複雜過程。

某些程度上,設計模式有點像是管理學原則,許多管理書籍會從過往歷史探索某些公司或機構,從各個角度提出某種關鍵因素,巧妙地與歷史串接,起來論斷公司或機構的興衰成敗,閱讀管理書籍只是瞭解有哪些角度可以切入管理問題,而不是那些角度就是最佳觀點或解決方案。在經營中的公司或機構採用某些管理原則,或避開某些管理方式,也並不保證公司或機構必然興盛,或免於敗亡。

無論是從閱讀文件瞭解設計模式,或是在閱讀程式碼時發覺採用了某些模式,都得先理解需求的情境、問題的核心,也要瞭解文件或程式中出現的設計模式實作,也許僅是可能的解決方案之一,有時同一問題在不同應用程式中採用的模式可能不盡相同,就如同有些管理原則在不同公司體系中可能是相對的矛盾。

閱讀管理書籍,是在瞭解不同公司或機構經營時會有哪些問題,瞭解設計模式,是在瞭解應用程式開發過程中可能會遭遇哪些需求,兩者的共同點,都是如何從過往中抓出問題的核心。

設計模式是語言重複用於某領域後,必然的產物

Gof模式最初是以基於類別、靜態的語言,作為解釋模式的實作語言,一度成為優秀設計和最佳實踐的代名詞,然而隨著一些動態、弱型別甚至基於原型的語言逐漸普及,面對這些本質上更彈性的語言,原本基於類別、靜態的Gof設計模式,逐漸成為複雜思路的代表,甚至某些開發者會持有「設計模式代表了語言本身存在缺陷,因而需要在設計上提供抽象思路」。

設計模式代表了語言本身存在缺陷嗎?如果從狹義的設計模式來看,基於類別、靜態語言的設計模式,有時在動態甚至非基於類別的語言看來根本微不足道,也可能有更簡單的設計方式,從這個角度來看,有開發者認為若語言本身提供良好機制,就不太需要特殊的設計。

然而從廣義來看,模式是一種重複出現的事件或物件題材,就軟體開發來說,只要語言用於某領域夠久或夠頻繁,就必然出現某些重複設計而形成模式,以模式來聲稱語言存在缺陷,其實暗指該語言用於某領域時必然有其擅長與欠缺之處。缺陷的說法只是相對的觀點,而不是絕對。

廣義的設計模式在思索問題本身,而不是狹義的語言機制。

舉例來說,Gof模式中有單例(Singleton)模式,狹義來說,是指在基於類別、靜態語言提供執行環境中獨一無二物件,這點就JavaScript來說,根本就不需要這個模式,因為JavaScript每個物件都是獨一無二,而且JavaScript本身沒有類別的概念,自然無法就狹義上來實現單例模式;然而廣義來說,單例模式意指的問題在於:如何在執行期間無論何時何地,透過某方式取得(或建構)的物件都是同一個實例。

不少開發者都曾遇過此類問題,也提出過幾種可實現的模式,例如若想呈現以下結果,你會如何解決?若其他開發者曾看過你的解決方式,也就可以循著相同的思路進行設計:

var s1 = new Singleton(); var s2 = new Singleton(); (s1 === s2); # 最後為true要如何實現?

JavaScript本身沒有類別,因而有幾種模擬類別的模式,從狹義的設計模式來看,那些模擬類別的模式是JavaScript本身存在缺陷的結果,但也正因為JavaScript沒有類別,才得以實現上述的單例模式,那麼該說是語言的缺陷或優點,才得以實現這樣的成果呢?

模式是用於啟發設計,而不是複製用的樣版

設計模式本身是現成的抽象概念,為了協助開發者理解,說明模式時會使用具體程式碼實作來呈現結構與行為,然而這些程式碼並非作為樣版,而僅是用來理解模式本身要解決的情境與問題。

在解決問題的過程中,透過既有模式來思考問題是否具有相似性,並透過模式來啟發類似的思考流程,從而設計出符合應用程式情境的實作,才能從設計模式本身獲得益處。如果只是將模式說明時的具體程式碼作為樣版複製至應用程式填充,如此運用設計模式只會有害而無益。

說明模式時的程式樣版,應只作為思考的起點而不是終點,程式樣版的每個細節都應思考其存在意義為何。對目前問題來說,類別繼承是必要的嗎?介面定義是必要的嗎?要不要使用工廠(Factory)來取代建構式?

這樣的思考方式,也正是模式存在的意義,無論是抽象概念或具體實作,與其從零開始思索,不如先從過去常見的問題設計開始著手,有時也會因此而看到同一問題的多種設計方式與優缺點,進而反過頭來由更多角度重新思考問題本身。

開發者間理解設計模式也是為了便於溝通,當開發者使用模式名稱進行溝通時,代表著開發者本身對模式名稱背後代表的問題、情境有所認識,也知道整組的抽象概念可用於解決問題,如果開發者雙方對模式名稱有所共識,當開發者A說到某處也許可用到策略(Strategy)模式時,開發者B就可根據策略模式的抽象概念,在腦海中啟發具體實作,而不用開發者A逐行撰寫程式碼來解說何謂策略模式,也就不會因此限制住開發者B的具體實作細節。

瞭解設計模式,應著重在理解問題本質
有些開發者初見某些模式,有相見恨晚的感覺,其實是由於曾有過類似問題的經驗,驟然遇到經過相當次數驗證的模式而心生感嘆,反思自己對問題的認識不夠深入,未曾從某些角度切入問題。

與此相反的狀況是,有時開發者本身經驗不足,無法認清模式背後的問題本質,將模式當成教條套用而不知變通,甚至將某種模式作為實作目標,希望讓程式碼看起來像某種設計模式,而不是實際地解決問題。

回過頭來看Gof模式,有些開發者認為Gof中有些模式具有啟發效果,有些模式則沒有實質意義。面對這樣的言論,應該深思他面對的問題領域為何?使用的語言又是什麼?該問題領域中哪些模式有啟發效果而哪些模式沒有?啟發的效果是什麼?毫無啟發的原因又是為何?

 

專欄作者

熱門新聞

Advertisement