2012年1月28日 星期六

軟體專案的素質之二 ─ 整體設計之 系統設計篇

上一次跟大家談的是專案管理,這次輪到談談整體設計的部分,整體設計主要分為系統設計,架構設計和圖形語言的有效使用,這些東東同樣地都是範圍相當大的題目,這次takeshi將先從系統設計講起...

由於takeshi本身是developer出身,自認為自己對這一塊領域和其他領域相比(專案管理和自動化工具)應該要來的更得心應手一點吧,同樣地takeshi再把心智圖放在底下供大家參考囉~
軟體專案的素質,本blog整理

如果朋友們有其它想法或意見,takeshi歡迎大家的意見ㄛ~

首先takeshi要先推薦之前在Kenming's鮮思維blog看到的好文「{程序員邀稿} 以架構為中心的主要設計產出(1)」(希望各位有興趣的朋友也能閱讀此文,會有很多收穫ㄛ),在此文中,Kenming提出了以下概念圖

就takeshi理解,Kenming在文章中提及,軟體系統開發應該由需求觀點開始,向user蒐集功能性和非功能性需求;然後再把蒐集到的需求帶進結構觀點,該觀點主要是處理系統的功能性需求,對問題領域(Problem Domain)作分析以導出領域模型(Domain Model);接著再進入實作觀點,該觀點主要是處理非功能性需求,考慮使用什麼樣的架構、平台或技術來支援領域模型的運作。

takeshi要提醒三點

1. 以上這三個主要步驟是使用覆往式(Iterative & Incremental)開發來進行的,一次覆往(Iteration),針對當下蒐集欲實作之需求(usecase)為主,走到下一個覆往,再慢慢重構(Refactoring)。

2.需求價值高(對user)和技術難度高(對開發團隊)的需求先作,這樣一來,專案走到越後面,會越穩定,可參考RUP的Elaboration和Construction之介紹。

3. 不要忘記專案管理和自動化測試與相關工具的支援ㄛ!

然而,takeshi自己在實務上的經驗,發現到很多開發團隊所採取的步驟如下
takeshi在實務上的觀察,本blog整理

由上圖可看出,這些開發團隊收到需求之後(需求觀點),立刻就開始決定技術平台和設計等,並開始實作(實作觀點),完全忽略了領域模型的存在(結構觀點),這樣的軟體開發步驟,會導致最終產出的系統缺乏彈性,以至於少許的需求變動就讓系統面臨大改的窘境;而系統不斷的面臨大改,導致系統的整體設計概念崩潰,而讓系統最終走入惡性循環的命運。

所以結構性觀點到底是什麼呢?takeshi認為結構性觀點其實是物件導向系統開發的核心,也就是領域模型(Domain Model),那領域模型到底是啥東東呢?takeshi可能要先稍微介紹一下程式語言的歷史演進,再跟大家說明到底啥是領域模型。

程式語言的演進簡史
從程式語言開始發展的1950年早期,到目前為止,軟體系統的開發方向是往越來越高階的抽象化層次
  • 機器語言(Machine Language)
電腦問世後的第一個程式語言,直接使用二進位表示,可由機器CPU直接讀取的語言,執行速度非常快,因為是由機器直接讀取,所以可讀性(human readability)低,故學習曲線相當高,且在不同的機器上,程式就得改寫,(不同的CPU吃的指令集不同),完全無可攜性(portability)可言。

  • 組合語言(Assembly Language)
為提高機器語言可讀性,一種便於記憶和理解的符號表示式,由組譯器(Assembler)轉譯成特定CPU的機器語言,然後再給其CPU執行,可攜性部分,是針對一種家族的CPU,如果是其它廠牌的CPU,轉譯出來的機器語言可能無法執行,目前在底層硬體操作或是驅動程式仍然可見到它的蹤跡。

  • 高階程式語言(High Level Programming Language)
為了提高程式設計師的生產力,故開發了可讀性更高的程式語言種類,由人類容易閱讀的英文單字組成,不同的機器有提供不同版本的編譯器(compiler),將撰寫好的高階語言吃進其編譯器,編譯成相對可執行之機器語言。

  • 結構式語言(Structured Programming)
高階程式語言的一種,具有三種特性,為依序執行(Sequence)、條件判斷(Selection)和反覆執行(Repetition),結構式語言通常是以演算法的資料結構和執行的流程與順序來解決問題領域的問題,故系統開發人員是從電腦運算平台的角度來看待問題領域。

  • 物件導向語言(Object-Oriented Programming)
高階程式語言的一種,除了包含結構式語言的特性之外,還另外具有三種特性,封裝(Encapsulation)、繼承(Inheritance)和多型(Polymorphism),物件導向語言可將問題領域之重要概念的相關資料與處理行為加以封裝,形成類別(Class),將概念相關之類別作一般化/特殊化(Generalization/Specialization)繼承處理,抽取出共用與非共用的程式碼區塊,最後也可藉由多型去改變類別行為的變異點,達到程式演算法共用的效果。故物件導向語言可將問題領域加以抽象化,將程式演算法從問題領域概念中抽離出來,系統開發人員便有機會從問題領域本身的角度來看待問題,接著才去思考運算平台的實作

由上簡史可看出,我們常說物件導向語言的特性就是封裝、繼承和多型,而它跟結構式語言之間的分野,最大的不同,就是可以幫助系統開發團隊可以使用一個更直覺、更有彈性的方式來開發系統,讓相關人員可以更聚焦在領域問題上!

有關於系統設計方面,近年主要採用的兩種設計風格如下
Transaction Script設計風格

Martin Fowler的定義如下
Organize business logic by procedures where each procedure handles a single request from presentation.

Transaction Script設計風格,本blog整理

簡言之就是SD依照SA開出的需求規格,重頭到尾地把相關設計與開發實作出來,然而這樣的設計風格,會不自覺地使得系統的架構與商業邏輯之程式碼綁在一個或多個需求上,如上圖所示,系統的每一個tier都綁在由需求延伸出來的紅線上,然而end user的需求是一天到晚都在變動的(I know it when I see it),所以Transaction Script設計風格的系統架構很容易就會因需求變動發生大範圍的影響,使得接受需求變動的彈性較低。

 Domain Model設計風格

依Martin Fowler的定義如下

An object model of the domain that incorporates both behavior and data.
Domain Model設計風格,本blog整理

此設計風格主要是強調在開發系統時,除了user的需求之外,其實我們還要更關注user所在的商業環境!因為user會提出需求,其背後是因為商業環境的驅動使然,所以系統除了要提供滿足user需求的服務之外,其核心應該建立在user背後的商業環境上,因為其和需求本身相較,是本質上穩定不變的部分。如此,即使user的需求天天在變,系統受到影響的部分將會有效減少,因為user的商業環境並不是天天在變動的。然而此種設計風格是需要SA大力支持的,因為需要有人作商業塑模,所以SA對於軟體工程的認知和系統抽象化思考是此設計風格事觀重要的一環。

此外,Martin Fowler也在『Patterns of enterprise application architecture』 一書中,提出了他對Stransaction風格和Domain Model風格的適用範圍,如下圖(質化,非量化)...

不同設計風格在不同系統複雜度之下所花費的心力比較圖,Patterns of enterprise application architecture
在上圖中的三條線各自代表不同的設計風格,其中Table Model 為.NET 相關技術,不在此討論範圍內,X 軸代表系統的複雜程度,Y 軸代表系統的維護心力,由此可看出Transaction Script 的設計風格是適合較小型、邏輯簡單的系統,而Domain Model 的是適合較大型、邏輯複雜的系統。

一般的作法是,當系統規模較小、商業邏輯相對簡單時,首先可以考慮先使用Stransaction style,來快速產出可運作系統(因為它初期所要花費的心力最小),但當系統欲處理之商業邏輯慢慢演變得越來越複雜、規模也越來越大時,takeshi建議趕快把系統重構成Domain Model style,來取得 面對複雜需求,仍然可以有效降低花費心力和提成系統彈性的能力。

如何使用Domain Model設計風格
Domain Model設計風格是目前takeshi在作系統設計的主力,所以本文章之後都聚焦在Domain Model上來作討論;另一方面,takeshi認為,Transaction style在實務上到處可見,有想要繼續討論的朋友可以跟takeshi說ㄛ~


Domain Driven Design
Domain Model設計風格是一種概念,換言之,可以實踐它的方式著實不少,takeshi目前實踐Domain Model的方式是遵照著Domain Driven Design社群的建議來作為設計的指導原則,有興趣的朋友們也可以參考此社群提供的功能完整的sample project(也有.NET版本的ㄛ);這個社群網站最初是起源於對一本書的讀書會,這本書就是『Domain-Driven Design: Tackling Complexity in the Heart of Software』,takeshi認為,此書是想要跨入Domain Model設計領域的朋友們,一定要讀的一本著作ㄛ,強力推薦~

FOG Design Pattern
這是欲加強自身功力的developer的必讀著作『Design Patterns: Elements of Reusable Object-Oriented Software』,這本書是討論design pattern的第一本書(1994年出版),也是作者的論文,所以有很多不易咀嚼的文章段落和少見的程式範例(Small Talk和C++),現今已有很多很友善的書籍來針對design pattern作討論,像takeshi的第一本design pattern的書是『Head First Design Patterns』,真的有好懂很多,強力推薦給有興趣的朋友們~

另外takeshi要再跟朋友們分享的是,takeshi在一開始學習design pattern時,一直找不到機會使用它們,過程中也和許多developer討論過,有些developer的論點是:「design pattern只是一種理論,實務上很難作發揮」,但經過takeshi多年下來的驗證 ,發現這完全是因為設計風格的問題!我們學了design pattern但使用不上的原因,完全是因為我們採取的是Transaction風格的關係,一旦我們採用了Domain Model風格,design pattern你想不用都難

實務經驗分享

takeshi使用Domain Model設計風格來實作專案大約經歷了兩個寒暑,其中也專案上線後由其他Developer接手維護的例子(當時takeshi已離開此工作環境了),當初takeshi在使用Domain Model設計風格來實作專案,希望能達到以下三點效益
1. 系統能有一個整體設計概念(核心)
在Domain Driven Design中提出,一個基於Domain Model作出的系統設計,會成為Ubiquitous Language,它記錄著此系統的核心概念;takeshi認為只要後手developer能搞懂這個由Domain Model組成的類別圖(當然還是要搭配循序圖和code),後手developer應該很快地就能搞懂此系統的商業邏輯全貌。

2.彈性大、擴充能力佳
由於系統提供的服務,是基於Domain Model之物件互動所產生的,一旦需求有變動,後手developer只要重新搭配物件互動的流程,即可完成變動之需求,系統彈性大 ;如果是新的需求, 後手developer也可以在domain model的整個繼承樹裡,找出變異的方法,透過繼承和多型的手段,來快速提供新的行為,而不須更動原本已撰寫好的物件互動流程,系統擴充能力佳。

3. 系統邏輯與使用者介面(User Interface,UI)完全隔離
由於系統的商業邏輯是基於Domain Model及其產生物件之互動所完成,所以跟系統的UI是完全沒有關係的(有效隔離);一旦End User要求變動UI,後手developer在更改UI時,完全不會影響到系統商業邏輯;如果是需要在UI上增加新的欄位,通常該欄位值也是Domain Model類別的特定屬性(Property)(也就是封裝),所以對後端系統商業邏輯也不會有大的影響(甚至沒有影響)。

而實際上,當後手developer在接手takeshi的專案時,的確要經過一段陣痛期(期間長度因人而異),因為從原本的Transaction設計風格要轉換到Domain Model設計風格,是一種思想轉換(paradigm shift),當通過陣痛期之後,經takeshi跟這位後手developer的確認,的確達到了takeshi一開始希望產生的效益!但對於沒有通過陣痛期的developer來說,他們大概會說:「這是什麼鬼東西,這種程式到底是怎麼寫出來的啊!」,對這些developer,takeshi也只能說:「你們的固執己見,有可能讓你們自己喪失了一個發現軟體開發新視界的機會ㄚ」。

10 則留言:

  1. Hello~
    倒數第二張圖的 "Domain Model 的設計風格",其實這樣還是有些問題的。

    應該是 Use Case Requirement 連結 "Requirement Function",而這往往是透過 Controller 這樣的角色來實現。

    Domain Model 是屬於 "fine-grained" 類型的物件,而 Controller 則是屬於 "coarse-grained" 類型。兩者是互補,Domain Object 是被封裝 "encapsulate" 起來的。

    歡迎有架構性議題,持續保持討論。^^

    回覆刪除
    回覆
    1. 關於"Domain Model 的設計風格"的圖,takeshi主要是想要表達出Domain Model放在系統架構中的"地理位置",讓文章的讀者先有一個全面性概觀,所以特意淡化了系統其它的部分

      kenming的建議,takeshi打算在之後發佈的文章再說明,簡述如下...
      基本上,takeshi在一種UI需求的案子中,通常會在controller tier把domain model組裝成UI的需求,response回client端
      假如是多種UI需求的專案,takeshi會多拆一個Data Transfer Object tier,給不同的UI使用,也就是說它們共用同一個Service tier

      takeshi經常在kenming的blog潛水,也經常拿kenming的blog內容跟同事們討論
      所以看到kenming的留言,takeshi真的感覺既驚訝又榮幸! ^^

      刪除
  2. 作者已經移除這則留言。

    回覆刪除
  3. 其實很單純,就是您那張圖,UC Requirement 連結到 "Service" 那一層,然後再連結至 "Domain Model" 即可。

    Service = Control = Control Object.
    Domain Model = Entity Object.
    UI = Data Transfer Object = DAO (Data Access Object) = Boundary Object.

    您可以思考上述的這三類型物件的位階與其作用。

    我喜歡討論軟體設計議題,只可惜因為速食文化,已少有人討論系統內部的架構問題。

    歡迎爾後多多就相關議題腦力激盪。:-)

    回覆刪除
  4. 圖改好了! 謝謝kenming的提醒~

    另外Kenming提醒的上述那三類型物件,takeshi大致上是了解的,也打算在之後的系統架構相關的文章來討論

    takeshi對OOAD是相當有熱忱的,因為這是改善軟體專案體質的必經之路ㄚ,雖然整體環境不是這麼理想,takeshi還是想努力從自身做起,希望也可以影響身邊的人,這是宅男工程師不切實際的浪漫吧 >"<

    往後也請Kenming也多多指教囉 Orz

    回覆刪除
  5. 那張圖的 business flow 相依(depend)於 domain model 看起來也不是很妥當的喔。 !^^

    雖然是有關連性,但若這麼說,它更是與 service layer 更有關係的。

    由 business flow 牽涉到系統的實作面 (realization),不太妥當,最好是藉由 use case 會比較理想。

    我曾經寫了一篇:大業務流程塑模的MSS三層次原則,您可以參考看看。更歡迎提出問題來思考。 ^^
    http://www.kenming.idv.tw/big_business_process_modeling_mss_3-layer_principle

    回覆刪除
  6. takeshi會去看看,謝謝kenming囉 ^^

    回覆刪除
  7. 由於Kenming的建議,takeshi把Domain Model的圖又作了一次改版,其實原本的的綠色虛線箭頭代表的是"產生",而不是"相依"啦;不過看了Kenming的留言後,takeshi覺得使用"相依"會比較直覺吧 :p

    這次的改版,主要是...
    1. Domain Model相依於Biz Flow
    2. Service Tier相依於User Requirements

    另外takeshi也去看了Kenming的MSS三層次原則,讓takeshi想起了Alistair Cockburn的User-Goal Level,不過Kenming提出的方法,有相對應的圖形可以去遵守,且原則清楚,takeshi有機會去試試看地,謝謝Kenming囉 ^^

    回覆刪除
  8. 其實我會建議您將原來的設計思維保留下來說,然後若有新增改版或是他人意見,再補充進來,做個對比。 !^^

    軟體設計圖這東西,要表現的是如何在廣度與深度間,抓去適切的焦點。

    我想會把上述一些觀念,並針對您的留言,會回覆在我的 blog 上。

    大家一同持續討論設計議題。 :-)

    回覆刪除
  9. kenming的建議我會注意的,謝謝kenming的指教^^

    回覆刪除