從2005年畢業至今,畢業已經11年,正好現處於離職狀態,所以理一理些年的一些技術,一些想法,在此也分享給大家,不對的地方也請大家儘管打臉,贊同的話呢,那就點個贊吧。:) 先說一下風格,一不照本宣科,二不複製粘貼,純手打,三必須是結合實際開發的經驗或理解來整理。 ...
從2005年畢業至今,畢業已經11年,正好現處於離職狀態,所以理一理些年的一些技術,一些想法,在此也分享給大家,不對的地方也請大家儘管打臉,贊同的話呢,那就點個贊吧。:)
先說一下風格,一不照本宣科,二不複製粘貼,純手打,三必須是結合實際開發的經驗或理解來整理。
對於面向對象不同的人會有不同的理解,我的理解對象就是一個實體,一個人,一個物,都是一個對象,萬事萬物有特點(屬性);有作用(方法);在某些條件下會有事情發生(事件);有生有死有過程(生命周期)。
然而對於面向對象的三個基本特征大家都是認同的,封裝,繼承,多態。 在很多面試場合也會頻頻問起,我們也就從這幾個特征聊起。
一、封裝
從字面意思理解來看就是把一個啥東西封住,然後裝起來,的確如此。從編程開發的角度來說,就是把一些常用的代碼提取出來封成方法/屬性/事件/委托等,裝在類/命名空間/程式集里。
舉個慄子
大家都知道的自動售賣機,好多地鐵站都有,我們去自動售賣機買一瓶可樂,使用支付寶掃一掃支付,可樂就從出貨口出來了,然後就是拿到手裡開喝。
- 我們不需要知道自動售賣機中的可樂到底存放在哪裡的;
- 我們不需要知道可樂是怎麼變冷的;
- 我們也不需要知道支付寶掃一掃它是如何傳輸數據,怎麼收款的;
- 我們必須遵從售賣機的操作規定,我們需要選擇可樂,掃一掃支付,可樂就到手了。
這就是封裝。我們有很多不需要知道的內容, 只需要按規定輸入內容,就能得到輸出結果,重點在於無需關註售賣機中的細節。
封裝的目的是什麼呢?或者說為什麼要封裝?
- 對外隱藏細節,便於維護。售賣機內如果某部件出現故障,維修時更換掉,對於使用者來說,就沒有任何影響,以前該怎麼購買就怎麼購買。
- 沒有那麼嚇人,讓用戶輕鬆使用。如果售賣機內部是透明的,我們將無法專心購買東西,很嚇人。如果汽車沒有擋住發動機,那麼發動起轉起來的時候會更嚇人。
- 同樣的使用規則,便於重用。由於隱藏了內部細節,我們只要按照規定操作即可,不管在那一臺機器操作都是一樣的,生產售賣機內部也可以完全一樣生產。對於代碼來說,封裝過的代碼,在哪裡調動方法都一樣的。
如何封裝?
編碼上的封裝:
- 將重用的代碼提取封裝成方法
- 將多種類似行為的Class封裝成泛型類
- 將穩定不變的行為對外提取成介面
- …
廣義上的封裝:
- 類庫及框架的封裝。比如常用的ORM框架:EF,NHibernate,iBatis(My Batis);微軟官方的.Net Farmework等等。
- UI界面與業務邏輯分離的封裝。比如常說的三層結構的模式,分離了表現層,業務邏輯層,數據層,其中表現層就可能是webform,windows from,Windows service,MVC web,然而他們的業務邏輯是一樣的,那麼我們就可以把相同的業務邏輯封裝起來,表現層調用同一個業務邏輯。
- 模塊化,插件化的封裝。比如我們的開發工具Visual Studio,安裝了Resharper,Nuget等插件,每一個插件都是一種封裝;我們開發一個windows form宿主程式,可以載入不同的插件/模塊,以豐富主程式的功能,每一個插件/模塊也是一種封裝。
封裝的原則:
- 常用場景驅動原則。得有一個常用使用場景,因為常用,所以才封裝出來大家用唄,在做的時候,我們往往也是找出一組場景及這組場景的樣例代碼開始封裝。如果僅僅在一些特定條件下才會使用的代碼,我們是沒有必要封裝一遍的,那不是自找麻煩嗎?也就是過度設計的臭味。
- 低門檻原則。封裝是為了更好的使用,我們要做到API簡單,如果封裝得“難用”,我想也只是搬石頭砸腳了,有的開發人員可能封裝了,命名不規範,參數繁多,那麼自己來說很好用,然而對於其他隊友來說完全無法使用,不懂參數意義,不懂方法作用,那麼這個封裝也是失敗的。
- 儘量自說明,有註釋有文檔。在簡單的使用場景中,一定讓封裝無需文檔也能使用;在複雜的使用場景中,一定要有註釋/文檔。
二、繼承
繼承就是繼續和傳承,和現實中的子承父業,財產繼承意思很接近。面向對象中的繼承更有特點:
- 繼承了父親的財產(方法,屬性,事件等),父親仍然擁有。
- 繼承了父親的財產可能發生變化(重寫),也可能是白條(抽象方法)。
繼承的意義何在?
- 個人理解繼承是實現面向對象的基礎,現實世界中的各種大類到小類,到具體的物種都是一個繼承關係,比如(動物->鳥類->燕子)。
- 行為/動作/特性繼承(介面實現):和類別繼承不一樣,是對不同類別的對象的相同行為/特性的提取。比如電腦的USB介面設備,USB滑鼠,USB鍵盤,USB印表機,它們是不同類別,它們有相同特性:支持USB接入電腦。所以可以抽象出它們的共同繼承介面:USB.
- 為面向對象的最最最好的理念——多態,打下堅實的基礎。
如何實現繼承
繼承的實現有兩種:類的繼承,介面的繼承。
這兩種繼承實現有不同的使用環境,所以我們引申出另外一個常見的問題:抽象類和介面有什麼異同?這個問題在很多面試場景也會出現。
抽象類和介面的相同點:
- 都不能直接實例化。
- 都可以繼承實現一些方法屬性等。
抽象類和介面的不同點:
- 很重要:抽象類繼承方向是相同的物種/類別的抽象,介面是一種規範或者是相同的行為/動作/特性的抽象。這一點很大程度決定了選擇抽象類還是介面,這可以作為一個基本的準則把。
- 抽象類只能繼承一個,介面可以繼承多個。
- 抽象類可以實現一些具體的方法,屬性,介面不能。
- 介面可以很好地實現回調,抽象類有局限性。因為抽象類只能繼承實現一個。
- 抽象類定義好以後,很容易修改;通常介面定義好後不能再修改,
繼承的原則
- 上邊的抽象類和介面第一條不同點作為選擇抽象類和介面的基本原則。
- 介面不變原則。定義好介面後,就不要變化了。否則第三方之前實現的介面會出問題。
- 面向抽象/介面編程。介面抽象行為,抽象類抽象類別/種類,抽象出各種變化,讓實體和實體之間依賴抽象,而不依賴具體。
- 介面定義一般以I開頭,所有定義“Public”。
- 預計可能會出現版本問題的時候,建議使用抽象類。如果採用介面,會強制要求舊版本的實現重新修改和編譯,抽象類則不會,增加的方法可以使用虛方法來實現預設方法。
三、多態
多態:多種姿態,並不是多種態度,呵呵。。多態個人認為是面向對象開發/設計的精髓所在。
也正是因為有了繼承,那麼我們的子類就可以以各種姿態出現(父類,父類的父類,基類,介面),這就是面向對象的多態。
多態的意義
- 應對變化。有了多態從而讓我們的開發,並不直接依賴具體的子類/實現,只需要依賴基類/介面,通過配置的方式,我們可以讓依賴的對象傳入基類/介面的不同實現,就可以贏多各種各樣的需求變化,從而實現以不變(介面/基類)應萬變。
- 增強擴展。因為不同的實現可以以相同的姿態出現,所以我們定義一個插件介面,這些插件就可以是想各種各樣的功能進來,增強程式的擴張性。比如Visual Studio的Resharper,Nuget等插件。
- 設計模式基礎。各種豐富多彩的GoF設計模式基本上都是基於多態而生。
多態的實現
多態的基礎是繼承,多態的實現也就是繼承的實現,分兩種:類繼承,介面實現。
多態的原則
- 通常類繼承的多態,會定義一些虛方法或者抽象方法。以便子類重寫方法。
- 因為繼承是多態的基礎,所以一定要遵循繼承的原則,實現好繼承才能實現好多態。