天天看點

關于 繼承、擴充和協定,深度好文

OC中protocol、category和繼承的差別以前還是有點迷糊,面試的時候說的有點混亂,現在結合一些資料總結一下。

利用繼承,多态是一個很好的保持“對擴充開放、對更改封閉”(OCP)的辦法,也是最常見的一種方法。Objective C還支援另外兩種文法來支援OCP:Protocol和Category。Protocol隻能定義一套接口,而不能提供實作,變相的也是一種Abstract class的實作方式(oc 文法上本身不支援抽象基類)。Category可以為類提供額外的接口和實作。那麼到底三者(繼承, Protocol,Category)在使用上到底有什麼本質的差別呢?在我看來,protocol的作用是為一些列類僅僅提供一套公用的接口,而完全沒 有辦法也沒可能去提供具體的一些實作情況;category則是為一個已有的類提供一些額外的接口和具體實作;而繼承則基于兩者之間,既可以想 protocol一樣提供隻是純粹提供接口,也可以像Category一樣提供完整的實作,而且繼承還能對類以後的功能進行改寫,是以說繼承的力量是最強 大的。那麼具體在使用的時候各自都适合什麼樣的情況呢?

        .        Protocol是定義行為而不管誰去怎麼實作,這是一種比較灑脫和不負責的情況,就好像在外包項目中的客戶一樣,他隻是他需要什麼什麼東西,具體實作他不會也不能給出一樣。delegate datasource這樣的就用protocol實作比較好

        .        Category是對一個功能完備的類的一種補充,就像是一個東西的主要基本功能都完成了,可以用category為這個類添加不同的元件,使得 這個類能夠适應不同情況的需求(但是這些不同需求最核心的需求要一緻)。找個就像你已經有了一輛能夠開動的汽車一樣,我們可以用Category為你的汽 車添加各種之前沒有的功能,最後讓這輛汽車變成超級跑車一樣。

        .        當某個類非常大的時候,Category可以按不同的功能将類的實作分在不同的子產品中實作。

        .        繼承則是都可以完成上面的工作,但是繼承有很大的代價問題,一是通過繼承來進行擴充是一種耦合很高的行為,對父類可以說是完全依賴;二是繼承由于 對父類依賴,是以開發代價相對大,要求對父類的工作流程相對熟悉;三是繼承體系如果太複雜會導緻整個系統混亂,難以維護。是以在能夠用上面兩種方法完成擴 展的時候,就千萬不要使用繼承。什麼情況才是迫不得已要使用繼承呢?那就是如果你既想提供一系列接口的定義,同時又想提供一些但是又不能提供全部的實作的 時候,這種情況就要使用繼承了。是以這麼看來繼承是對上面兩種功能的一個黏合劑。

關于category的另外一些見解:

        .        雖然category可以通路類的執行個體變量,去不能建立新的執行個體變量,如果要創新的執行個體變量,請使用繼承;

        .        在category中,不提倡對原有方法進行重載。原因非常簡單,在category中進行重載,無法對原方法進行通路,而繼承中可以使用super。如果真的需要對原方法進行重載,請考慮繼承,比如我要定義一個繼承自UIViewController的類,就不能用Category,因為,這我定義的這個類中,我要實作UIViewController中的viewDidLoad、init等方法,用了category後父UIViewController中的這些方法将無法被調用;

        .        一個類可以定義多個category,但是如果不同category中存在相同方法,編譯器無法決定使用哪個category;

        .        在定義category時,我們可以僅僅給出方法定義,而不需要給出具體的實作。這在程式增量開發時是非常有幫助的;

        .        category是可以被繼承的。在某個父類中定義了category,那麼他所有的子類都具有該category;

        .        在需要為某個類建立私有成員方法時,也用category的方式來實作。

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

        .        當在Category中覆寫一個繼承的方法,在Category中的方法可以通過向super類發送一個消息來調用被繼承的方法。但是,如果Category中覆寫的那個方法已經在這個類的其它Category定義過了,則之前定義的方法将沒有機會被程式調用

        .        在Category中無法确定其能夠可靠的覆寫某個方法,而這個方法已經在其它的Category中定義過。這個問題在使用Cocoa架構時尤其 突出。當你想覆寫某個架構已經定義好的方法時,該方法已經在其它Category中實作,這樣就無法确定哪個定義和實作會被最先使用,帶來很大的不确定 性。

        .        如果你重新覆寫定義了一些方法,往往會導緻這個方法在整個架構中實作發生了變化。舉例來說,如果你增加了NSObject中 windowWillClose:的實作,這會導緻所有的視窗調用那個新實作的方法,進而改變所有NSWindows執行個體的行為。這會帶來很多不确定性, 并很有可能導緻程式的崩潰。

http://bbs.itheima.com/thread-117162-1-1.html

繼續閱讀