標籤:OOAD

書摘與心得 軟體預先架構之美學 Prefactoring

中文書名:軟體預先架構之美學

英文書名:Prefactoring

ch1

基本觀念

  • Extreme Abstraction

  • Extreme Separation

  • Extreme Readability

另一面向是把焦點放在介面(interface);想著介面,就能進一步邁向抽象化的目標,也就是說,要去想哪些元件能為你所用,而不是去想這些元件該怎麼運作。

預構無法保證你的設計不用做重構。

情境是一切。不同的路要準備不同裝備,而且要知道終點在哪理。

保持敏捷。

ch2

探討需求時,採用精簡過的使用案例(Use Case)或故事板(Stroy Board)記錄情境,用它跟客戶討論。在我們的團隊中,如果BA未寫Use Case,則用它跟BA討論。

不要重新發明輪子。如果系統的某個部份已有成熟、可靠的元件可用,就不要再重新開發。

系統中每個概念都要定義清晰的名詞。

結合兩個概念,比分離一個概念簡單許多。

把資料凝結可以簡少必須劉在心中的概念數量。例如:把street, no, state凝結成address。

用抽象資料型態(Abstract Data Type),建立模型時,避免使用基本資料型態。用顯態定型(explicit typeing)描述問題和解法,日後有需要時容易轉成隱態定型,但反向則困難許多。

避免一切都是字串。

不要只是為了實作上的考量而把類別合併起來。

快速的做出雛型。

ch3

從大藍圖開始

系統裡的設計決策應該和架構一致。

建立系統前,先對既存環境有所認知,可協助你節省開發時間。

介面規範

介面和使用該介面的人之間有個規範。規範的內容包含了該介面中每個方法的先決條件(precondition)和後續條件(postcondition)。先決條件主張,每個方法被呼叫時必為真,使其能執行運算,後續條件主張方法執行完後,應為真。

作者傾向於被呼叫的方法會特別在其程式中查核其先決條件。這些程式就是規範的記錄。

查驗

應該把來到系統的輸入資料,轉成其對應的抽象資料型態。

所有識別項目都應含有自我查驗值,以防止多數常見資料錯誤。

就分層系統而言,查驗工作都是在各層的邊界上,至於查驗什麼,則是根據每層之間所定義的規範。

把假設和決策形諸文件

錯誤處理和錯誤

找出你的系統會有那些誤差和錯誤,並決定如何處理。

誤差和錯誤發生時,回報給使用者的訊息應該對使用者有意義。訊息中應該包含盡可能多的資訊,說明該如何避開或更正問題。

ch4

系統初次成品完工時,我們想保有最少功能集(minimum feature set,MFS)。

過成

分析的中點是描述使用者需求(不是規格),而設計的重點是建立一套解決方案,使其滿足需求。

分析停滯

你不能花一輩子的時間,確認你的系統需求,你應該花時間瞭解基本的抽象化內容、基本的使用案例、以及每件事之間如何互動。

設計停滯

要確認類別的設計是否足夠,方式之一是利用你考量的類別運作使用案例,如果所有案例都能實施,很可能你已經抓住所有有意義的類別了。

初始設計

除非某人試著使用該概念,否則該概念不會被完整理解。

從大處規劃,從小處設計

如果我們的設計結果無法執行某些使用案例,繼續走下去之前,就得重做一遍。

有了最初一組類別之後,我們檢視每個使用案例,想弄明白會不會影響我們選配出來的類別。

檢測功能

系統開發的每個時點,你都應該建立檢測內容的草案,檢測內容可以包含基本的使用案例,以及日後討論時會出現的任何誤用案例。

AT與UT都要寫,而且最好可以自動化。

安全

安全不能事後諸葛,隨時都要考量進去。

LSP – Liskov Substitution Principle

子型別必須可以替換它們的父型別

如果對於每個型別為S的物件o1,都存在一個型別為T的物件o2,使得在所有以T定義的程P中,以o1取代o2時,P的行為維持不變,則S為T的子型別。

假設有一個函式f,帶有一個指向某個基礎類別B的pointer或reference作為參數,同時假設有某個B的衍生類別D,將D轉型成B後傳遞給f,會導致行為不正常,那麼D就違反了LSP。顯然D在f中是脆弱的。

f的作者會嘗試為D加入某種測試,以使得當D傳遞給f時,f能行為正確。這個測試違返了OCP,因為現在f對於不同的B衍生類別都不具有封閉性,這樣的測試就是程式碼的壞味。

違返LSP就是潛在違返OCP。

“IS-A"太過廣義了,不能當作subtype的定義。

OCP – Open Closed Principle

軟體實體(如類別、模組、函式等等)對擴充應保持開放性,而對修改應維持封閉性

如果OCP運用得宜,未來程式中單一的變更是以增添新程式碼的方式達成,而不是修改已經運作正常的舊有程式碼

  1. Open for extension
    表示模組的行為可以加以擴充。當應用程式的需求改變時,我們可以擴充模組,使它具有能夠滿足這些變更的新行為。
  2. Closed for modification
    擴充模組的行為並不會對模組的source code或binary code造成變更,模組的binary可執行版本會聯結的.jar檔都不會變動

利用抽象化獲得明確的封閉性

Note:Strategy與Templete Method是滿足OCP最常見的手法

不論是哪一種模組,都不可能完全封閉,因此設計者必須選定他的設計要對哪一類的變更「封閉」,他必須猜測(運用經驗與對該產業使用者、需求的了解)最有可能的變更類型,並建構出抽象關念來保護自己免於變更的干擾

遵循OCP的代價是昂貴的,要花費很多時間心力來創造抽象概念,這些概念會增加軟體的複雜度,且開發人員對抽象概念的承擔力是有極限的。因此,我們希望把OCP應用在有可能發生的變更上。

為了使軟體免於不必要的複雜度,我們可以允許被愚弄一次,這表示我們最初設計時並不預期它會改變,當改變發生,我們就能實作出能使我們免於受這類未來變更所影響的抽象概念。

在繼續開發下去之前,我們想知道有可能發生哪些變更類型,找出可能發生變更的等待時間越久,就越難創造出適當的抽象概念,因此,我們可用以下方法讓改變快點發生:

  1. 優先編寫測試
  2. 極短開發週期,幾天而非幾週
  3. 先開發功能而後開發基礎建設,並經常向專案關係人展示這些功能
  4. 先開發最重要的功能
  5. 及早且經常發佈軟體,儘快經常的向客戶與使用者展現

遵循OCP會帶來物件導向宣稱最大的益處(彈性,可重用性與可維護性),但要符合此原則並非只用OOPL就能達到,做過多的抽象化也並非好主意,抗拒草率的抽象化跟抽象化本身一樣重要

SRP – Single Responsbility Principle

類別變更的原因應僅只有一種

每一個responsbility都是改變的軸心,當需求改變,這種改變會透過類別間的responsibility變化顯露出來,如果一個類別負擔一個以上的responsibility,那麼它改變的理由就會超過一個。

如果一個類別有一個以上的responsibility,那這些responsibility就會coupled在一起,改變一個 responsibility就可能會損害或抑制此類別滿足其它responsibility的能力。這樣的couple導致了脆弱的設計,會在作變更時 以不可預期的方式出現問題。

Note:Facade或Proxy有助於切割不常變動的永續層與businesss rule