軟體開發流程概要-
《Head First OOA&D
》讀書筆記(1)
滿足使用者期望-《Head First OOA&D》讀書筆記(2)
這是最後一篇了,主要就是一些很實際的物件導向的設計原則,以及一些Coding過程中需要注意的事情。這應該是程式員們最感興趣的部分了。其實就我的觀點,這部分是比較好掌握的,畢竟這些知識你看到了,實踐了,也就學會了。相反對於項目初期的準備工作就很難有具體的原則作為參考,因為不同的項目之間的區別太大了,客戶的特點,組織圖,溝通方式都是不同的。也就是涉及到更多的交流和協調工作,需要有很強的管理能力,也就是soft skills。一旦前期工作做好了,舞台搭好了,基調定準了,後續的工作就很好開展了。
下邊就說說這些快被說爛了的OO的概念,我這裡比較介紹的比較簡要也不完整,詳細的可以去WikiPedia查。
1. 基本概念
· 封裝(Encapsulation)
基本的概念就是,把與外界無關的資料和邏輯放在與外界隔絕的環境中,在外界看來依然比較有條理,不至於太複雜,所以也叫做資料隱藏(Data Hiding)。這樣可以避免內部的資料和實現被外界修改或者幹擾。這裡我們就需要有一些OO的直覺,能夠識別出有關聯的一組資料和方法,然後把它們封裝成獨立的類。
比較進階的考慮封裝就是,封裝變化(Encapsulate what varies)。為什麼這麼做呢?因為我們要把問題簡化,減小可預期的變化對整體系統的影響,增加系統的穩定性和可維護性。因此就要儘可能把要變化的東西限制在足夠小的範圍內。封裝也不能濫用,把任何東西認為會變化,而要進行封裝,那會使系統的複雜性增加而沒有太多的價值。
· 繼承(Inheritance)
繼承使得我們可以基於其他已有的類的基礎上建立具備相同屬性和方法的子類,並可以根據需要改寫或增加這些屬性和方法。這樣實現了代碼複用,避免了重複代碼的出現,對於變化的要求能夠很輕鬆的應對。使用繼承的時候要盡量把具體方法,屬性的定義提到高的層次上來。但是繼承會造成類數量過多的情況,可以用代理,組合或彙總的方法來使代碼更容易維護。
繼承常常被用在對虛類或介面的實現上,這樣做可以避免模組之間的耦合過於緊密。對於介面和虛類的區別,我們可以用屬於Is-A 還是 Has-A的關係來判斷。一個使用繼承的原則就是,面向介面,而不是面向實現編程(Programming to interface rather than implementation.)做到這點,我們就可以使我們的系統處於松耦合的狀態,易於擴充,滿足變化需求。當然,最終我們的系統肯定是要構造真正的實體類的,不可能任何地方都不用new的。
· 多態(Polymorphism)
多態實現了一個類的子類可以當作它的父類使用。這樣我們能夠得到更大的靈活性,設想一下。你的類中使用到了飛機這個類的對象當作參數傳入,那麼對於直升機,戰鬥機,波音飛機這些子類就都是可以使用的,而他們的行為確都稍有不同。
2. 設計原則
· OCP(Open Close Principle)
對擴充開放,對修改封閉,這句話也有很多人說了。為何要如此呢?首先,隨著外界要求的變化,我們的代碼肯定是需要修改的,因此我們必須承認變化的存在。而我們又不想看到我們的代碼在不停的變化之中,變得不穩定,影響已有的使用者。因此,我們就要保證,所有的修改不能改變已有的功能。也就是不能夠,修改已有的方法,屬性的實現代碼。那怎麼做才能滿足變化的需求呢?擴充!擴充包括,派生子類,提供滿足變化的新的實現。使用組合(Composition)和彙總(Aggregation)可以避免因繼承出現過多的類,也能達到對現有功能的擴充的效果。
· DRY(Don’t Repeat Youself)
避免重複代碼。出現重複代碼或者功能相同的函數,方法並不是僅僅意味著我們多做了一些不必要的工作,而是對於將來的維護都是一個負擔,甚至是個風險。因此我們要保證我們的所有的屬性,行為,資料只在我們的系統中一個地方定義,實現,而且是最合理的地方。記住,我們不是僅僅為了保持代碼簡潔。
· SRP(Single Responsibility Principle)
單職責原則。SRP要求我們要是我們的每一片程式碼片段都要足夠簡潔,一個方法只做一件事。一個類只有一個職責。換句話說我們的每個類只能有一個導致它變化的原因(One reason to change)。這樣做的好處不言而喻,就是能夠使我們的類輕鬆面對變化,而且易於維護,可讀性好。
SRP和DRY原則是相輔相成的。SRP保證了每一個類都是職責單一的,而DRY則保證了這個類在系統中只能有一個,任何處理這類資訊的需求都會用到這個類。
· LSP(Liskov Substitution Principle)
Liskov替代原則。LSP給了我們一個檢驗繼承關係合理性的一個工具。任何子類都要能夠放在使用父類的環境中使用,而且不能出現致命錯誤。使用LSP檢驗的結果,會使我們找到很多不適合使用繼承的情境。其實也就是我們的子類變化太大了,它和父類的區別已經明顯多過他們的相似,我們就要考慮使用其他方法解決這個問題了。比如代理(Delegation),這個可不是C#中的代理,他的意思是不用繼承,而是用其他類的實體的引用的方式,來把一些工作分給那個有相似功能的類。這樣可以起到繼承相同的效果。
· other
其他還有很多的原則的,但是大家通過上邊這幾個也能夠自己發揮出來一些。其實無外乎就是對OO基本概念的延伸。比如依賴倒置原則(DIP),說我們不能讓高層模組依賴於底層模組,而要使用抽象,讓高層模組依賴於抽象,底層模組實現抽象。其實也就是對繼承或多態的擴充,和OCP有類似作用。
3. 設計模式及其它
很多人都喜歡學習設計模式,還要常常掛在嘴上不停念叨以免忘了。真的有這麼神奇嗎?其實瞭解了這些OO的概念和原則,你就會發現,這些設計模式都是對於一個特定情境採用OO來進行分析得出的最精簡的一種解決辦法。這種辦法既不是唯一的也不是放之四海而皆準的。當然,我們瞭解了這些設計模式,當我們遇到這些情境的時候可以毫不費力的拿來使用,可以節約很大的力氣。另一方面,這種典型應用情境常常很少,我們實際的項目中常常會有很多的限制,不同的因素,因此我們就不能直接照搬了,而是依照這些OO原則和概念,來活學活用。要記住,設計模式會是你的程式變得複雜,可讀性變差。使用設計模式增加擴充性,靈活性的同時是以犧牲簡潔性,易讀性為代價的。
還是那句話,所有的原則和概念都是死的,都是供人所用的。因此,我們要首先能夠識別出哪些是要變化的,哪些是需要抽象的,那些是其他模組不關心的,然後才能使用這些原則。對於那些不會變化的情況,或者是暫時不便的情況大可以就用最簡單的方式解決問題。當我們發現有變化出現的時候再考慮這些原則也為時不晚,這叫做Refactoring to pattern, 沒有人能夠一次把所有問題都找出來。對於Refactoring的方法和時機的問題,我會找機會單獨討論。