iOS 類簇(class clusters)

類簇(class clusters)

類簇是Foundation framework框架下廣泛使用的一種設(shè)計模式。它管理了一組隱藏在公共抽象父類下的具體私有子類。

沒有使用類簇(Simple Concept but Complex Interface)

為了說明類簇的結(jié)構(gòu)體系和好處,我們先思考一個問題:如何構(gòu)建一個類的結(jié)構(gòu)體系用它來定義一個對象存儲不同數(shù)據(jù)類型的數(shù)字(char,int, float, double)。因為不同數(shù)據(jù)類型的數(shù)字有很多共同點(例如:它們都能從一種類型轉(zhuǎn)換成另一種類型,都能用字符串表示),所以可以用一個類來表示它們。然而,不同的數(shù)據(jù)類型的數(shù)字的存儲空間是不同的,所以用一個類來表示它們是很低效的??紤]到這個問題,我們設(shè)計了如下圖1-1的結(jié)構(gòu)解決這個問題。

圖1-1 Number類結(jié)構(gòu)

Number是一個抽象父類,在其方法聲明中聲明了子類的共有操作。但是,Number不會聲明一個實例變量存儲不同類型的數(shù)據(jù),而是由其子類創(chuàng)建對應(yīng)類型的實例變量并將調(diào)用接口共享給抽象父類Number。
到目前為止,這個類結(jié)構(gòu)的設(shè)計十分簡單。然而,如果C語言的基本數(shù)據(jù)類型被修改了(例如:加入了些新的數(shù)據(jù)類型),那么我們Number類結(jié)構(gòu)如下圖1-2所示:

圖1-2 更復(fù)雜的Number類結(jié)構(gòu)

這種創(chuàng)建一個類保存一種類型數(shù)據(jù)的概念很容易擴(kuò)展成十幾個類。類簇的體系結(jié)構(gòu)展示了一種概念簡潔性的設(shè)計。

使用類簇(Simple Concept and Simple Interface)

使用類簇的設(shè)計模式來解決這個問題,類結(jié)構(gòu)設(shè)計如圖1-3所示:

圖1-3 類簇

使用類簇我們只能看到一個公共父類Number,它是如何創(chuàng)建正確子類的實例的呢?解決方式是利用抽象父類來處理實例化。

創(chuàng)建實例(Creating Instances)

在類簇中的抽象父類必須聲明創(chuàng)建私有子類變量的方法。抽象父類的主要職責(zé)是當(dāng)調(diào)用創(chuàng)建實例對象的方法時,根據(jù)調(diào)用的方法去分配合適的子類對象(不能選擇創(chuàng)建實例對象的類)。
在Foundation framework中,你可能調(diào)用類方法或者allocinit創(chuàng)建對象。以Foundation framework的NSNumber創(chuàng)建數(shù)字對象為例:

NSNumber *aChar = [NSNumber numberWithChar:’a’];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];

使用上面方法返回的對象aChar, anInt, aFloat, aDouble是由不同的私有字類創(chuàng)建的。盡管每個對象的從屬關(guān)系(class membership)被隱藏了,但是它的接口是公開的,能夠通過抽象父類NSNumber聲明的接口來訪問。當(dāng)然這種做法是及其不嚴(yán)謹(jǐn)?shù)模撤N意義上是不正確的,因為用NSNumber方法創(chuàng)建的對象并不是一個NSNumber的對象,而是返回了一個被隱藏了的私有子類的對象。但是我們可以很方便的使用抽象類NSNumber接口中聲明的方法來實例化對象和操作它們。

擁有多個公共抽象父類的類簇(Class Clusters with Multiple Public Superclasses)

在上面的例子中,使用一個公共抽象父類聲明多個私有子類的接口。但是在Foundation framework框架中也有很多使用兩個或兩個以上的公共抽象父類聲明私有子類接口的例子,如表1-1所示:

類簇 公共抽象父類
NSData NSData,NSMutableData
NSArray NSArray,NSMutableArray
NSDictionary NSDictionary,NSMutableDictionary
NSString NSString,NSMutableString

還存在這種類型的類簇,但這些清楚說明了兩個公共抽象父類是如何協(xié)同工作來聲明類簇的編程接口的。一個公共抽象父類聲明了所有類簇對象都能相應(yīng)的方法,而另一個公共抽象父類聲明的方法只適合允許修改內(nèi)容的類簇對象。

創(chuàng)建子類(Creating Subclasses Within a Class Cluster)

類蔟的體系結(jié)構(gòu)是在易用性和可擴(kuò)展性之間均衡的結(jié)果:類簇的應(yīng)用使得學(xué)習(xí)和使用框架中的類十分簡單,但是在類簇中創(chuàng)建子類是困難的。但是很少情況下需要在類簇中創(chuàng)建子類,因為類簇的好處是顯而易見的。

如果你發(fā)現(xiàn)類簇提供的功能不能滿足你的變成需要,那么在類簇創(chuàng)建子類是一種選擇。例如:假如你想在NSArray的類簇中創(chuàng)建一個基于文件存儲而不是基于內(nèi)存存儲的數(shù)組。因為改變了類的底層存儲機制,就不得不在類簇中創(chuàng)建子類。

另一方面,在某些情況下我們創(chuàng)建一個類內(nèi)嵌類簇對象就足夠了。例如:如果你的程序需要被提醒,當(dāng)某些數(shù)據(jù)沒被修改的時候。在這種情況下,創(chuàng)建一個包裝Foundation framework框架定義的數(shù)據(jù)對象的類可能是最好的方法。這個類的對象能干預(yù)修改數(shù)據(jù)的消息,攔截這個消息,對這個消息采取相應(yīng)的行動,然后重定向給內(nèi)嵌的數(shù)據(jù)對象。

綜上所述,如果你想管理對象的存儲空間,就在類簇中創(chuàng)建子類。否則,創(chuàng)建一個復(fù)合對象(composite object),復(fù)合對象:你自己設(shè)計的類對象內(nèi)嵌一個標(biāo)準(zhǔn)Foundation framework框架的對象。

真正子類(A True Subclass)

在類簇中創(chuàng)建一個子類,你必須:

  • 創(chuàng)建類簇中抽象超類的子類
  • 聲明自己的存儲空間
  • 重寫父類的所有初始化方法
  • 重寫父類的所有原始方法(primitive methods)

第一點:因為在類簇的體系結(jié)構(gòu)中只有類簇中的抽象父類是公開可見的節(jié)點。第二點:子類會繼承類簇的接口但沒有實例變量,因為抽象父類沒有聲明,所以子類必須聲明它所需要的任意實例變量。最后:子類必須重寫繼承的所有方法。

一個類的原始方法(primitive methods)是構(gòu)成其接口的基礎(chǔ)。拿NSArray為例,它聲明類管理數(shù)組對象的接口。在概念上,一個數(shù)據(jù)保存了很多數(shù)據(jù)項,它們都能通過下標(biāo)(index)訪問。NSArray通過這兩個原始方法表達(dá)了這一抽象概念,countobjectAtIndex:,以這些方法為基礎(chǔ)可以實現(xiàn)其它派生方法。

派生方法(Derived Method) 可能實現(xiàn)(Possible Implementation)
lastObject 查找數(shù)組對象中的最后一個對象:[self objectAtIndex: ([self count] –1)]。
containsObject: 查找對象通過遍歷數(shù)組對象給其發(fā)送objectAtIndex:消息,直到數(shù)組里的所有對象都檢測完為止。

原始方法(primitive methods)和派生方法(derived methods)的接口區(qū)分使創(chuàng)建子類更簡單。子類必須重寫所有繼承的原始方法(primitive methods),這樣做可以確保所有繼承的派生方法(derived methods)都能正常運行。

原始和派生的區(qū)別同樣適用于完全初始化對象接口。子類中需要解決如何處理init…方法的問題。

通常,一個類簇的抽象父類方法聲明了一系列init…方法和+ className類方法?;谀氵x擇的init…方法或+ className類方法,抽象類決定用哪個具體的子類來實例化。你可以認(rèn)為抽象類是為子類服務(wù)的,因為抽象類沒有實例變量,它也不需要初始化方法。

自定義的子類應(yīng)該聲明自己的init…+ className方法,不應(yīng)該依賴?yán)^承的方法。為了保持初始化鏈,它應(yīng)該在自己的指定初始化函數(shù)里調(diào)用父類的指定初始化函數(shù)(designated initializers)。在類簇中它也應(yīng)該以合理方式重寫繼承的所有初始化方法,抽象父類的指定初始化函數(shù)總是init。

復(fù)合對象(A Composite Object)

在你自定義的對象中內(nèi)嵌一個私有的類簇對象稱為復(fù)合對象。復(fù)合對象可以利用類簇對象來實現(xiàn)基本的功能,只攔截復(fù)合對象想要特殊處理的消息。這種結(jié)構(gòu)減少了代碼量,利用了Foundation framework的測試代碼。如圖1-4所示:

圖1-4 composite object

復(fù)合對象必須聲明它自己是類簇抽象父類的子類,必須重寫父類的所有原始方法,也可以重寫派生方法但不是必須的。

總結(jié)

在Cocoa中,實際上許多類都是以類簇的方式實現(xiàn)的,即它們是一群隱藏在通用接口之下與實現(xiàn)相關(guān)的類。例如創(chuàng)建數(shù)組時可能是__NSArray0,__NSSingleObjectArray, __NSArrayI,所以請不要輕易嘗試創(chuàng)建NSString,NSArray,NSDictionary的子類。對類簇使用isKindOfClassisMemberOfClass的結(jié)果可能是不正確的。因為類簇是由公共抽象類管理的一組私有類,公共抽象類并不是實例對應(yīng)的真正的類,類簇中真正的類的從屬關(guān)系被隱藏了。

參考文獻(xiàn)
Class cluster
Class Clusters

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,689評論 1 32
  • 這里是對《設(shè)計模式Java版》[https://gof.quanke.name]的提煉匯總,在真正深入理解之前,方...
    LeonXtp閱讀 1,169評論 0 0
  • 整理來自互聯(lián)網(wǎng) 1,JDK:Java Development Kit,java的開發(fā)和運行環(huán)境,java的開發(fā)工具...
    Ncompass閱讀 1,627評論 0 6
  • 一:java概述: 1,JDK:Java Development Kit,java的開發(fā)和運行環(huán)境,java的開發(fā)...
    慕容小偉閱讀 1,959評論 0 10
  • 今天周五,為明天后天的修復(fù)關(guān)系公開課做準(zhǔn)備!
    鄢瑜含閱讀 382評論 6 2

友情鏈接更多精彩內(nèi)容