標籤:

淺談架構——面向對象

寫在開始:本篇算是淺談架構系列的正式文章了,計劃整個系列文章從面向對象編程開始到設計模式、應用級架構設計模式、到以SOA思想為基礎的產品線架構設計為止。每一個部分的編寫思路是先列舉一些知識相關的理論或設計原則,以及自己對這些原則的理解。接著會依照原則提出一些通用的或自己總結的設計方法和注意事項,這些方法都是我在工作或學習過程中驗證過,不能保證這些都是正確的甚至可以說有些東西都是錯誤的,但至少他們在某一個時間段或者在某個場景下幫助我解決了一些實際的問題。這裡藉助這個平台把手裡的磚頭拋出去,萬一能引出來一大堆璞玉來的話就賺了。如果大家發現什麼不對或者不贊同的地方,請一定要留言或者發到我的郵箱modify961@126.com,一塊來討論一下,也可以加我的微信:y376214298。在這裡先各位大神了。

這篇文章的標題叫「面向對象」,本來是想起名叫「面向對象程序設計」。但是寫著寫著就發現程序設計太大了,不是短短几個段落就能夠概括的,借用《代碼大全2》中的一個標題來說"設計是個無章法的過程(即使它能得出清爽的成果)」,設計本來就不是一個一蹴而就的事情,整個過程應該是在不斷的設計、評估、討論、試錯、測試代碼以及修改測試代碼中不斷演化而來的。自己本身能力就不夠,如果強行要用幾百個字說明白的話,就有點為賦新詞強說愁的意思了。但是我會在接下來的系列文章里會用一些實例或者曾經做過的設計來試著總結一下對程序設計的理解,並試著抽取一下關於程序設計的方法或原則。

閑話少敘,這篇文章從什麼是面向對象、面向對象的三大特徵、如何進行對象的抽取設計以及面向對象的原則四個方面來說一下我理解的面向對象。

什麼是面向對象

前段時間部門在開職責劃分工作會的時候總工說。你們都是開發轉的管理,都理解什麼是面向對象編程。什麼是面向對象編程哪?無非就是封裝繼承多態,咱們這次做職責劃分就是一次封裝。部門層面把每個職位做一個概括說明這就是封裝,你們各個生產線再根據自己的實際需要選擇自己需要的職位職責詳細說明並細化到每個人這就是繼承和多態。如果那個部門項目緊或者人員進行跨生產線調動我的要求就是就位以後很快就能有符合公司要求的產出,這個就是可替換原則。

確實是這樣的,面向對象與其說一種工具不如說是一種思考方式,是用程序設計的眼光看待現實的世界。把現實世界的事物按照一定的方法和規則不同層次上進行抽象,在抽象的時候我們只需要關注事物的核心屬性和行為而忽略其他的細節。開發人員將這些屬性和方法再體現在一個個的模塊、類或者子程序裡面,通過他們之間有序的調用,最終完成現實世界在計算機里的構造。這裡的不同層次是指在不同的視角下,比如上面的職責劃分從單個員工的層面來說可能需要考慮個人的現有能力,發展意願、潛力等,如果從生產線的層面來說就需要考慮生產線產品對技術的要求,人員的配置等,從部門的層面來說可能就需要考慮成本、產值等其他的事情了。如果對應到編程實現上可能就是需要在子程序介面、類介面以及模塊介面這些層面進行考慮了。

面向對象的三大特徵

面向對象的三大特徵是封裝性、繼承性、多態性。但是我認為繼承和多態應該是因果的關係,因為面向對象可以繼承所以有了多態。這裡就把繼承和多態放到一塊來說一下。

先說封裝,封裝就是隱藏細節,讓使用者只看到被允許看到的,不能看到任何實現的細節。那麼如何去把握那些可以被允許那些是細節那,或者換個說法就是如何進行有效的封裝那?有一個最簡單的辦法就是先把可訪問性降一級看看能不能實現功能。比如把public 變為protected或者private試試,如果可以那就降。另外就是不要暴露成員數據,對於需要暴露的也優先考慮只提供讀介面。如果覺的這個也不好把握的話,那就採用介面+實現類的編程方式。但是我覺的這樣會增加很多多餘的文件而且也有點偏離介面的用途(下結會詳細說一下介面和抽象類),所以我建議對於模塊來說只對外提供一個介面或一組小的介面,內部實現類的話設置為庫內(C#)或包內(Java)可見就行了,不用專門的創建一個介面(這種情況只適用於一個人開發的模塊,多人合作的話建議對模塊再次細分,直到可以明細化到每個人去實現)。

另外還有一個需要注意的地方就是不要去假設封裝對象的使用者,只對上下文(模塊)或參數(類:如果類所屬模塊有上下文的話建議類的參數也要是上下文)負責,這樣也可以盡量減少對象與對象之間的耦合度。

再說一下繼承,封裝可以是模塊層面的也可以說是類層面的。但繼承的話一般就是指類與類之間的一種特殊關係了。他的作用就是一個類對另一個類的細化,他們擁有共同的介面、一部分共同的數據成員和內部實現。繼承能夠把這些放到一個基類裡面,避免了代碼和數據的重複。本人並不太贊同普通類之間的繼承。一般優先考慮介面的繼承,其次考慮抽象類的繼承,實在沒有別的選擇情況下再考慮普通類之間的繼承。在使用繼承的時候也要注意下一些事情,第一個就是繼承的類要全部實現基類的介面。第二個就是對於能抽取的方法都抽取到基類裡面。最後一個也是最重要的一個就是不要用多重繼承。另外如果一個基類只有一個實現類的話就不要繼承了,在一個類裡面實現就行了。那麼在什麼情況下會建議使用繼承,就是在類裡面有太多的swith case或者 else if的時候就可以考慮繼承了。

基類被子類繼承後,每個子類都是基類的一個形態,這種不同形態就是多態。上面說到「如果一個基類只有一個實現類的話就不要繼承了」。所有也可以說只要有繼承就有多態

如何進行對象的抽取設計

上面講到了面向對象以及面向對象的三大特徵。那麼在我們實際運用中如何按面向對象的方法對事物進行抽象和構建。我認為應該考慮以下的一些步驟。在第二段的時候說程序設計是一個反覆迭代不斷試錯的過程。所以這些步驟只是一個建議,不必按特定的順序來,他們也可能被反覆的使用。相信每個成熟的程序員都會有自己一套成熟的設計思路的。步驟共分為兩個部分識別現實的對象以及用程序構造對象

1、從不同層次對現實世界進行抽象,提取出他們的核心屬性以及核心行為。

2、確定對象之間的功能邊界以及依賴關係。

3、定義對象的核心屬性(數據)和操作(方法)。

4、定義對象的公有和私有部分。

5、定義介面及其所需要的參數,並抽取成上下文。

6、定義主要子程序及其實現細節。

面向對象的部分原則

古代官場將做事要三思而行,三思就是思危,思變,思退,凡事從這三個方面綜合考慮最終來決定如何應對將要處理的事情。同樣我們在進行面向對象編程的的時候也有一些可以參考的標準。實際開發的時候需要根據這些標準權衡利弊以後再決定如何進行設計開發。同樣的道理當我們去判斷一個程序一個模塊或一個類好壞的時候也是要從這些標準來進行考慮判斷。面向對象有六大設計原則在這裡我只撿比較重要的四個來說一下。另外兩個在核心思想上和這四個有些重複,所以就略過不提了。

1、單一職責原則:這裡不能把它理解為只做一件事情,而是要結合上面對象的功能邊界來說,應該是只做對象功能邊界內的事情。功能邊界的選擇決定了封裝的質量,邊界太大了容易造成對象內聚性的減弱,而範圍太小又容易造成系統對象過多進一步造成系統耦合性過高。

2、開發封閉原則:這裡的開放是指對對象的操作方法修改的開放,當有新的需求變化就可以對現有的代碼進行修改和擴展。封閉是有兩個方面,一個是範圍的封閉,一但對象的範圍確定了就不能再進行任何修改了,另外一個封閉是指對介面的封閉,可以慎重的進行介面的增加,但是對於已發布的介面修改是要儘力避免的或者是絕對禁止的。

3、里氏替換原則:它的核心思想就是 對於基類中定義的所有子程序,用它的任何一個子類時他們的介面含義都應該是相同的。比如一個FileDownload基類以及FtpDownload、ShareDownload、HttpDownload三個子類。使用者應該能夠任意調用三個子類里的download介面來進行文檔下載。

4、介面隔離原則:它的核心思想是不要定義一個大的介面,而要使用一組小而專業的介面。看定義貌似是和單一職責原則有點衝突,其實他們是從不同的角度來對抽象的審視,單一性原則是從封裝的角度保證對象的純潔性,而介面隔離原則是從對外開放的角度來考慮,讓使用者只使用可以使用的那一部分功能。比如:對一件代售的商品而言,作為買方只需要查看他的價格,但是對於店主來說除了可以看價格以外還可以修改它的價格,查看和修改都是對這一個對象的操作,但對於不用的用戶來說就有不同的操作許可權了。


推薦閱讀:

面向對象剖析
淺談高內聚低耦合
守弱的內涵和外延
PCIE匯流排的地址問題

TAG:軟體架構 |