- 抽象類別是實作類別的 placeholder。
- 抽象類別定義行為,而其子類別實做該行為。
- 每當在兩個或兩個以上的地方找到共同行為時,小心將該行為抽取到一個類別裡,然後以此共同類別,重利用這項行為。
以下使用書中分析設計範例來說明原本的設計
為了增加曼陀林(Mandolin,一種與 Guitar 性質差不多的樂器)的支援,於是將 Mandolin 與 Guitar 的共用特性抽取出來,變成以下的設計
然而,既然 Guitar 有 GuitarSepc,當然 Mandolin 也要有自己的 MandolinSpec 囉!
現在有了 GuitarSpec 與 MandolinSpec,下一個步驟要做的就是將兩者的共同特性給抽取出來成為一個獨立的 class
重新設計之後,成功了加入對 Mandolin 的支援,以下是完整的設計圖
- 要看看軟體是否設計良好的最佳方式之一,是試著改變它。
- 什麼是介面(interface) ?
此程式概念具有兩種角色:
(1) 定義要應用在多個型別上的行為
(2) 作為使用那些型別之類別在使用時的焦點 - 對介面(interface)撰寫程式碼,而不是對實作;這會讓你的軟體更容易被擴展。
- 透過對介面(interface)撰寫程式碼,你的程式碼將可以使用該介面的所有子類別 - 甚至那些還沒有被建立的子類別。
- 什麼是封裝(encapsulation) ?
在防止更多維護問題上,它是當仁不讓的,超過史上任何其他 OO 原則。做法是:透過讓物件行為變化所需的改變「區域化」。 - 封裝可以幫助你保護類別免於不必要的改變,因此應該要將總是變化之物封裝起來。
- 什麼是變更(change) ?
每個類別都應該試圖確保這件事的發生只有一個理由,這件事代表許多設計不良的軟體片段之死。 - 應用程式中的每一個類別只有一個理由改變
2008年11月22日 星期六
[Head First OOAD] 諸行無常(良好設計) & 給你的軟體 30 分鐘的伸展操(彈性的軟體) 1/2
2008年11月16日 星期日
[Head First OOAD] 將你的軟體帶進現實世界:分析
- 分析幫助你確保系統運作在真實世界的情境裡。
- 以對客戶、老闆、與自己合理的方式,撰寫你的使用案例。
- 分析與使用案例,讓你對客戶、經理、和其他開發者展示系統在真實世界的情境裡如何運作。
[Head First OOAD] 山可移,此情永不渝...現在,情況有變:需求變更
觀念說明
- 從第一步到最後一步通過使用案例的完整路徑,稱為使用情節(scenario)。
- 大部分的使用案例有一些不同的使用情節,但總是共有相同的使用者目標。
- 任何時候你的使用案例改變,你必須回頭檢查你的需求。
- 有時候,需求的變更揭露出關於系統你所不知道的問題。
- 變更是常態,隨著你每次的時作,系統總是隨之改善。
常見問題
何謂替代路徑
替代路徑是包含在使用案例裡的一或多個步驟,是選擇性的(optional)或提供替代性的(alternate)方式通過使用案例。
替代路徑可能是增加到主要路徑裡的額外步驟,或是提供步驟,讓你以完全不同於主要路徑的方式,到達使用案例的目標。
可以在相同使用案例中擁有一個以上的替代路徑 ?
你可以有多條替代路徑提供額外的步驟,以及多重條件從起始條件(start condition)通往終止條件(stop condition)。
甚至可以有替代路徑早一點終止使用案例。
重點整理
- 需求將總是隨專案進行而改變。
- 當需求變更時,你的系統必須隨之演進,處理新需求。
- 當你的系統需要以新的或不同的方式運作時,就從更新你的使用案例開始。
- 一個使用情節是通過使用案例的單一路徑,從開始到結束。
- 單一使用案例可以有多重使用情節,只要每個使用情節都具有相同的客戶目標。
- 替代路徑可以是只發生在某些情況下,或者提供完全不同路徑通過使用案例的一部份的步驟。
- 你應該總是試著避免重複程式碼。那是維護工作的夢魘,並且是系統設計的問題點。
[Head First OOAD] 給客戶所需之物:收集需求
關於使用案例(Use Case)
- 系統運作的正確與否是由客戶決定的,因此,假如你漏掉某項需求,或者他們忘了跟你提,系統還是沒有正常運作。
- 談到需求時,最好讓客戶自己說。仔細聆聽,注意系統需要作什麼:稍後在整理出系統要如何做那些事。一組好需求不止於客戶告訴你的是,甚至在不尋常或是出乎意料的環境與使用情況下還要能夠確保系總是能運作。
- 使用案例是人們對系統為讓某事發生所採取步驟的稱呼。A use case describes what system does to accomplish a particular customer goal.
- 使用案例是捕捉新系統或軟體變更的潛在需求之技術。
- 每個使用案例提供一個或多個使用情節(scenario),傳達系統如何與終端使用者(enduser)或其他系統互動,完成特定目標。
使用案例可分為三部分
清楚的價值(clear value)
每個使用案例都必須對系統有清楚的價值。假如使用案例無助於客戶達成目標,這個使用案例便沒有什麼價值。
起點與終點
每個使用案例都必須有明確的起點(starting point)與終點(stopping point),某件事開始此流程,然後有條件(condition)指明流程已完成。
外部啟動者
每個使用案例由外部啟動者(external initiator)開啟,有時啟動者是人,有時可能是系統外的任何事物。
使用案例的相關疑問
- 使用案例的關鍵要點之一,是把焦點放在完成一個特定目標上;若系統作一件以上的事情,那就需要一個以上的使用案例。
- 使用案例企圖幫你了解系統應該作什麼 - 往往可以像別人(例如:客戶、老闆)解釋該系統;因此使用案例應該用簡單、日常生活的語言。
- 使用案例與使用案例圖(Use Case Diagram)是不同的。
- 使用案例通常是步驟清單,而使用案例圖是以視覺化的方式顯示使用案例。
- 使用案例的目的不在於說明如何撰寫程式碼。
重點名詞
External Initiator:啟動使用案例所描述的步驟清單,沒有它,使用案例是不會開始進行的。
Use Case:幫助你收集好需求,並詳述系統作什麼。
Start Condition:這通常是使用案例的第一步。
Requirement:系統要成功所必須做到的事情。
Clear Value:沒有它,使用案例對人就沒有價值;沒有它,使用案例通常會失敗。
Stop Condition:讓你知道使用案例何時結束。沒有它,使用案例會永遠繼續下去。
Main Path:當一切正常運作時,系統在做的事。這通常是客戶談論系統時所描述的事。
需求
- 好的需求確保你的系統如客戶所預期的那樣運作。
- 確認需求涵蓋系統的所有使用案例。
- 運用使用案例找出客戶忘了告訴你的事情。
- 使用案例將揭露任何不完整、闕漏的的需求,你可能必須將它們家到你的系統裡。
重點整理
- 需求是系統為了正確運作所要作的事。
- 最初的需求通常來自客戶。
- 為了確保你有一組好需求,要發展出系統的使用案例。
- 使用案例詳述系統確切應該作什麼。
- 一個使用案例具有單一的目標,但內含多重的路徑,到達此目標。
- 好的使用案例具有起始與終止條件、外部啟動者、並且對使用者具有清楚的價值。
- 一個使用案例就是一則系統如何運作的故事。
- 對系統要達成的每一個目標,你至少會有一個使用案例。
- 在使用案例完成後,你可藉由他精鍊並增加你的需求。
- 確保所有使用案例皆可行的需求清單是一組好的需求。
- 你的系統必須運作在真實世界裡,而不只是在你預期的狀況中。
- 當事情有錯時,你必須有替代路徑,到達你的目標。
2008年11月15日 星期六
[Head First OOAD] 偉大軟體由此開始:良好應用程式之基石
開發偉大軟體的三個步驟
- 確認你的軟體作客戶要它作的事情 (此步驟把重點放在客戶上,確保應用程式作它應該先作的事。這裡是收集需求(requirement)與分析(analysis)工作所著力之處)
- 應用基本的 OO 原則,增加軟體的彈性 (一旦軟體開始運作,你能找出任何可能因疏忽而產生的重複程式碼,並且確認你再使用良好的 OO 編程技術)
- 努力達成可維護、可重利用的設計 (得到作它該作之事的良好物件導向應用程式? 是時候了,運用設計模式與 OO 原則,確保你的軟體準備就緒)
enum 的妙用
在 class 的某些屬性,若是只有幾種可能性,則可以用 enum 來處理。
Q1:什麼是 enum ?
Ans:enum 是列舉型別,C、C++ 或是 Java 5.0 以上都有支援。
以 color 為例,有可能會有 red、blue、yellow、green … 等等,但種類絕對不會有太多種 (特殊應用例外!),這種情況就相當適合使用 enum;簡單來說,只要具有「具有標準範圍或合法值」的特性,都適合用 enum 來表示,可以避免取得壞資料。
以下用一段範例程式碼來說明:
Q2:使用 enum 的好處有哪些呢 ?
Ans:使用 enum 的 method 或 class,會受到它的保護,確保不會出現未定義在 enum 中的值,即使是打錯或是拼錯,compiler 也會提醒改正。不僅可以確保 type 的安全,也可以確保 value 的安全。
比較以下兩張 class diagram 的設計:
原本的設計
使用 enum 後的改變
基本 OO 原則的思維
1、物件應該作其名稱所指之事
假如物件名稱為 Jet,它可能應該 talkOff() 與 land(),而不應該 takeTicket() - 那是其他物件的工作,不屬於 Jet。2、每個物件應該代表單一概念
不要讓物件服務雙重或三重責任。避免使用一個 Duck 物件,同時代表會呱呱叫的真正鴨子或是黃色的塑膠鴨。3、未使用的特性是無用的贈品
假如你有一個物件,經常具有空值或 null 的特性,你可能有一個物件在作一種以上的工作。假如物件中的某個特性很少有值,為何該特性是此物件的一部份? 要不要有一個較好的物件,僅使用原有特性的子集(subset)?仔細留意前面的 Inventory class diagram,每次顧客要作 search() 的動作時都必須要傳入一個 Guitar 物件,但這個物件的 serialNumber 與 price 永遠都不指定(正是上面第三點說的未使用特性),所以衍生出一個問題:
既然每次都不指定,那可以另外制訂一個 class 來專門處理 Guitar 的規格嗎?
當然是可以的,因為在書中就提出了一個 GuitarSpec 類別來解決這個問題:
- GuitarSpec 所做的事就只有負責與規格相關的事情,其他一概不管。
- GuitarSpec 就是只能代表吉他的規格,也不會突然可以代表小提琴的規格。
- 客戶在進行搜尋時,可以專注於規格,無用的 serialNumber 與 price 就可以丟掉囉!
- 重點是:程式碼不重複。
委派(Delegation)
委派是當物件需要執行某項工作時,不是直接進行該工作,而是要求另一個物件代為處理。
委派可讓程式碼更能重利用,也讓每個物件關注自己的功能,而不是把處理單一物件行為的程式碼分散在整個應用程式。
委派讓每個物件自己去擔心相等性(或某種其他工作)。這表示物件彼此獨立(鬆散隅合;loosely coupled)。
- loosely coupled 的物件可以從一個應用程式取出,並輕易的在另一個應用程式中重利用,因為他們並未緊密繫結到其他物件的程式碼。
- loosely coupled 意味著應用程式裡的各個物件各有特定工作要作,而且只作那項工作。
- loosely coupled 的應用程式通常比較有彈性,且容易變更。因為每個物件都相當獨立於其他物件,因此可以改變一個物件的行為,而不必牽動所有其他物件,如此一來增加新功能或特色就變的容易多了。
當 GuitarSpec 內容改變時
當 GuitarSpec 中增加屬性(numStrings)時,以先前設計來說,會有什麼結果呢?
- 將與 GuitarSpec 相關的事務都封裝進 GuitarSpec class 中。
- 將 Inventory 的 search() method 委派由 GuitarSpec class 來處理。
重點總結
- 脆弱的應用程式是很容易出錯的。
- 運用類似封裝或是委派的 OO 原則建造有彈性的應用程式。
- 封裝將應用程式分解成合乎邏輯的零件。
- 委派將處理特定工作的責任轉交給另一個物件。
- 總是透過客戶要什麼,來啟動你的專案。
- 一旦完成基本功能,就重新精鍊你的設計,讓它有彈性。
- 有了符合功能並具有彈性的設計,便能運用設計模式,進一步改善你的設計,讓應用程式更容易重利用。
- 找出應用程式經常改變的部分,試著將它們與其他不改變的地方相分離。
- 建造運作無誤但設計不良的應用程式,滿足了你的客戶,卻留給自己痛苦,以及無數的修正問題的不眠夜。
- 物件導向分析與設計(OOA&D)提供一種產生設計良好之應用程式的方法,同時滿足客戶與程式設計師。
訂閱:
文章 (Atom)