繼承,Protocol,Category

轉自:http://xubenyang.me/tag/category

開放封閉原則(OCP)就是,“對擴展開放,對更改封閉”。是所有面向對象設計的一個核心宗旨。感興趣的可以看百度百科的一些解釋:http://baike.baidu.com/view/2493421.htm

在用ObjectiveC進行開發的時候,OCP當然也是宗旨。利用繼承,多態是一個很好的保持OCP的辦法,也是最常見的一種方法。ObjectiveC還支持另外兩種語法來支持OCP:Protocol和Category。(想要了解Objectivec語法的可以看這裏:http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html)。

Protocol只能定義一套接口,而不能提供實現,變相的也是一種Abstract class的實現方式(oc語法上本身不支持抽象基類)。Category可以爲類提供額外的接口和實現。那麼到底三者(繼承,Protocol,Category)在使用上到底有什麼本質的區別呢?在我看來,protocol的作用是爲一些列類僅僅提供一套公用的接口,而完全沒有辦法也沒可能去提供具體的一些實現情況;category則是爲一個已有的類提供一些額外的接口和具體實現;而繼承則基於兩者之間,既可以想protocol一樣提供只是純粹提供接口,也可以像Category一樣提供完整的實現,而且繼承還能對類以後的功能進行改寫,所以說繼承的力量是最強大的。那麼具體在使用的時候各自都適合什麼樣的情況呢?

  1. Protocol是定義行爲而不管誰去怎麼實現,這是一種比較灑脫和不負責的情況,就好像在外包項目中的客戶一樣,他只是他需要什麼什麼東西,具體實現他不會也不能給出一樣。delegatedatasource這樣的就用protocol實現比較好
  2. Category是對一個功能完備的類的一種補充,就像是一個東西的主要基本功能都完成了,可以用category爲這個類添加不同的組件,使得這個類能夠適應不同情況的需求(但是這些不同需求最核心的需求要一致)。找個就像你已經有了一輛能夠開動的汽車一樣,我們可以用Category爲你的汽車添加各種之前沒有的功能,最後讓這輛汽車變成超級跑車一樣。
  3. 當某個類非常大的時候,Category可以按不同的功能將類的實現分在不同的模塊中實現。
  4. 繼承則是都可以完成上面的工作,但是繼承有很大的代價問題,一是通過繼承來進行擴展是一種耦合很高的行爲,對父類可以說是完全依賴;二是繼承由於對父類依賴,所以開發代價相對大,要求對父類的工作流程相對熟悉;三是繼承體系如果太複雜會導致整個系統混亂,難以維護。所以在能夠用上面兩種方法完成擴展的時候,就千萬不要使用繼承。什麼情況纔是迫不得已要使用繼承呢?那就是如果你既想提供一系列接口的定義,同時又想提供一些但是又不能提供全部的實現的時候,這種情況就要使用繼承了。所以這麼看來繼承是對上面兩種功能的一個黏合劑。

之所以會想到這些,是因爲自己在寫一個webservice加載庫的時候得到的體會。這個庫已經提供了一系列最基本的加載webservice的功能,但是不同的程序webservice的api和類型都不是相同的,要怎麼才能應用到不同程序中去呢?之前我一直想都不想直接繼承了,產生一個新的webservice加載器,然後對不同api分別進行實現。但是每次這麼做的時候就發現繼承的時候做了很多重複而且沒有什麼創造性的工作。今天才突然發現了問題的所在,既然我的webservice庫已經是一個功能基本齊備的加載器了,爲什麼我不簡單的爲這個加載器在不同的程序中創建不用的category呢,然後在複用一些已有的api,不就能夠完成工作了嗎?確實,我現在通過category的方式減少了之前太多太多的重複勞動,真恨自己爲什麼沒有提早注意到這個問題。

關於category的另外一些見解:

  1. 雖然category可以訪問類的實例變量,去不能創建新的實例變量,如果要創新的實例變量,請使用繼承;
  2. 在category中,不提倡對原有方法進行重載。原因非常簡單,在category中進行重載,無法對原方法進行訪問,而繼承中可以使用super。如果真的需要對原方法進行重載,請考慮繼承,比如我要定義一個繼承自UIViewController的類,就不能用Category,因爲,這我定義的這個類中,我要實現UIViewController中的viewDidLoad、init等方法,用了category後父UIViewController中的這些方法將無法被調用;
  3. 一個類可以定義多個category,但是如果不同category中存在相同方法,編譯器無法決定使用哪個category;
  4. 在定義category時,我們可以僅僅給出方法定義,而不需要給出具體的實現。這在程序增量開發時是非常有幫助的;
  5. category是可以被繼承的。在某個父類中定義了category,那麼他所有的子類都具有該category;
  6. 在需要爲某個類創建私有成員方法時,也用category的方式來實現。

Category不能完全代替子類,有以下幾個最大的缺點:

  1. 當在Category中覆蓋一個繼承的方法,在Category中的方法可以通過向super類發送一個消息來調用被繼承的方法。但是,如果Category中覆蓋的那個方法已經在這個類的其它Category定義過了,則之前定義的方法將沒有機會被程序調用
  2. 在Category中無法確定其能夠可靠的覆蓋某個方法,而這個方法已經在其它的Category中定義過。這個問題在使用Cocoa框架時尤其突出。當你想覆蓋某個框架已經定義好的方法時,該方法已經在其它Category中實現,這樣就無法確定哪個定義和實現會被最先使用,帶來很大的不確定性。
  3. 如果你重新覆蓋定義了一些方法,往往會導致這個方法在整個框架中實現發生了變化。舉例來說,如果你增加了NSObject中windowWillClose:的實現,這會導致所有的窗口調用那個新實現的方法,從而改變所有NSWindows實例的行爲。這會帶來很多不確定性,並很有可能導致程序的崩潰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章