官方文檔中“類簇”的翻譯

類簇 在官方文檔中的解釋

官方文檔中的解釋

以下是翻譯

類簇

類簇是Foundation框架廣泛使用的設(shè)計(jì)模式。類簇在公共抽象超類下對多個私有的具體子類進(jìn)行分組。以這種方式對類進(jìn)行分組簡化了面向?qū)ο罂蚣艿墓部梢婓w系結(jié)構(gòu),而不會降低其功能豐富度。類簇是基于抽象工廠設(shè)計(jì)模式的。

沒有類簇的情況:簡單的概念,復(fù)雜的接口

為了說明類簇結(jié)構(gòu)及其優(yōu)點(diǎn),考慮構(gòu)建定義對象以存儲不同類型(char、int、float、double)數(shù)量的類層次結(jié)構(gòu)的問題。因?yàn)樵S多不同類型具有許多共同特征(例如,它們可以從一種類型轉(zhuǎn)換為另一種類型,并且可以表示為字符串),所以它們可以由單個類表示。但是,它們的存儲要求不同,因此用同一個類表示它們的效率很低??紤]到這一事實(shí),我們可以用下圖所示的類結(jié)構(gòu)來解決這個問題。

image

Number是抽象超類,在其方法中聲明其子類共有的操作。但是,它不會聲明實(shí)例變量來存儲數(shù)字。子類聲明了這樣的實(shí)例變量,并在聲明的編程接口中共享Number。

到目前為止,這種設(shè)計(jì)相對簡單。但是,如果考慮這些基本C類型的常用修改,則類層次結(jié)構(gòu)圖看起來更像下圖。


image

簡單的概念 - 創(chuàng)建一個容納數(shù)字值的類 - 可以很容易地發(fā)展到十幾個類。類簇結(jié)構(gòu)提供了一種反映概念簡單性的設(shè)計(jì)。

使用類簇的情況:簡單的概念,簡單的接口

將類簇結(jié)構(gòu)應(yīng)用于此問題會產(chǎn)生下圖的類層次結(jié)構(gòu)(私有類為灰色)。


image

此層次結(jié)構(gòu)的用戶只能看到一個公共類,Number那么如何分配正確子類的實(shí)例呢?答案就在于抽象超類處理實(shí)例化的方式。

創(chuàng)建實(shí)例

在類簇中,一個抽象的超類必須聲明用于創(chuàng)建其私有子類的實(shí)例的方法。超類負(fù)責(zé)根據(jù)您調(diào)用的創(chuàng)建方法分配正確子類的對象 - 您不會,也不能手動選擇實(shí)例的類。
在Foundation框架中,通常通過調(diào)用 +className...方法或alloc...init...方法來創(chuàng)建對象。以Foundation框架的NSNumber類為例,您可以發(fā)送這些消息來創(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];

你不需要負(fù)責(zé)釋放從工廠方法返回的對象。許多類也提供了創(chuàng)建需要你自己管理釋放對象的標(biāo)準(zhǔn)alloc...init...方法。
每個對象返回的aChar、anIntaFloataDouble可能屬于不同的私有子類(事實(shí)上確實(shí)如此)。雖然每個對象的類成員關(guān)系都是隱藏的,但是它的接口是公共的,是由抽象超類NSNumber聲明的接口。雖然不完全正確,但是將aChar、anInt、aFloat和a.對象看作NSNumber類的實(shí)例是很方便的,因?yàn)樗鼈兪怯蒒SNumber類方法創(chuàng)建并通過NSNumber聲明的實(shí)例方法訪問的。

具有多個公共超類的類簇

在上面的示例中,一個抽象公共類聲明了多個私有子類的接口。這是一個純粹意義上的類集群。也有可能并且通常需要有兩個(或可能更多)抽象公共類來聲明集群的接口。這在Foundation框架中很明顯,其中包括下圖中列出的集合。

image

其他的用到這種類型的集合也存在,但這些集合清楚地說明了兩個抽象節(jié)點(diǎn)如何協(xié)作將類編程接口聲明為類簇。在每個集合中,一個公共節(jié)點(diǎn)聲明所有集合對象都可以響應(yīng)的方法,另一個節(jié)點(diǎn)聲明僅適用于允許修改其內(nèi)容的集合對象的方法(譯者注:換個簡單的說法-可變和不可變的集合)。
集合接口的這種因子分解使面向?qū)ο罂蚣艿某绦蚪涌诟有蜗?。舉個例子,想象一個表示聲明此方法的book的對象:

- (NSString *)title;

book對象可以返回自己的實(shí)例變量或創(chuàng)建一個新的字符串對象并返回它 - 這無關(guān)緊要。從此聲明中可以清楚地看出,返回的字符串無法修改。任何修改返回對象的嘗試都會引發(fā)編譯器警告。

在類簇中創(chuàng)建子類

類簇體系包含了簡潔性和可擴(kuò)展性之間的權(quán)衡:一小部分公共類代表大量私有類,使得在框架中學(xué)習(xí)和使用更容易,但在集合中創(chuàng)建子類有點(diǎn)困難。然而,如果很少需要創(chuàng)建子類,那么集合結(jié)構(gòu)顯然是有益的。在符合上述這些條件的情況下,集合(類簇)在Foundation框架中使用。

如果您發(fā)現(xiàn)類簇不提供程序所需的功能,那么子類可能是合理的。例如,假設(shè)您要創(chuàng)建一個數(shù)組對象,其存儲基于文件,而不是像在NSArray類簇中一樣基于內(nèi)存。因?yàn)槟诟念惖幕A(chǔ)存儲機(jī)制,所以您必須創(chuàng)建一個子類。

另一方面,在某些情況下,定義一個,在類簇中嵌入對象的類,可能就足夠了(也更容易)。假設(shè)您需要在修改某些數(shù)據(jù)時,在程序中被提醒。在這種情況下,創(chuàng)建一個包裝Foundation框架定義的數(shù)據(jù)對象的簡單類可能是最好的手段。此類的對象可以干預(yù)修改數(shù)據(jù),攔截消息,對其進(jìn)行操作,然后將它們轉(zhuǎn)發(fā)到嵌入數(shù)據(jù)對象的消息。

總之,如果您需要管理對象的存儲,就請創(chuàng)建一個真正的子類。否則,就去創(chuàng)建一個復(fù)合對象,該對象將標(biāo)準(zhǔn)Foundation框架對象嵌入到您自己設(shè)計(jì)的對象中。下面的部分提供了有關(guān)這兩種方法的更多詳細(xì)信息。

真正的子類

您在類簇中創(chuàng)建的新類必須:

  • 是集合的抽象超類的子類
  • 聲明自己的存儲空間
  • 覆蓋超類的所有初始化方法
  • 覆蓋超類的原始方法(如下所述)

因?yàn)榧旱某橄蟪愂羌簩哟谓Y(jié)構(gòu)中唯一公開可見的節(jié)點(diǎn),所以第一點(diǎn)是顯而易見的。這意味著新的子類將繼承集群的接口,但不會繼承實(shí)例變量,因?yàn)槌橄蟪惵暶鳠o。因此第二點(diǎn):子類必須聲明它需要的任何實(shí)例變量。最后,子類必須覆蓋它繼承的任何直接訪問對象實(shí)例變量的方法。這種方法稱為原始方法。

類的原始方法構(gòu)成了其接口的基礎(chǔ)。例如,使用NSArray類,該類聲明接口到管理對象數(shù)組的對象。概念上,數(shù)組存儲了許多數(shù)據(jù)項(xiàng),每個數(shù)據(jù)項(xiàng)都可以通過索引訪問。NSArray通過它的兩種原始方法來表達(dá)這種抽象概念,count并且objectAtIndex:。以這些方法為基礎(chǔ),可以實(shí)現(xiàn)其他方法衍生方法 ; 表1-2給出了派生方法的兩個例子。

方法 可能的實(shí)現(xiàn)
lastObject 通過向此消息發(fā)送數(shù)組對象來查找最后一個對象:[self objectAtIndex: ([self count] –1)]。
containsObject 通過重復(fù)向數(shù)組對象發(fā)送objectAtIndex:消息來查找對象,每次遞增索引,直到測試了陣列中的所有對象。

基元和派生方法之間的接口劃分使得創(chuàng)建子類更容易。您的子類必須重寫繼承的基元,同時要確保它繼承的所有派生方法都能正常運(yùn)行。

原始派生的區(qū)別適用于完全初始化的對象的接口。如何init...的方法需要被掌握的問題還需要被處理。

通常,集群的抽象超類聲明了許多init...+className方法。如創(chuàng)建實(shí)例中所述,抽象類基于您的選擇init...+ className方法決定實(shí)例化哪個具體子類。您可以認(rèn)為抽象類聲明了這些方法以方便子類。由于抽象類沒有實(shí)例變量,因此不需要初始化方法。

您的子類應(yīng)該聲明它自己的init...(如果它需要初始化它的實(shí)例變量)和可能的+ className方法。它不應(yīng)該依賴于它繼承的任何東西。為了在初始化鏈中維護(hù)其鏈接,它應(yīng)該在自己指定的初始化方法中調(diào)用其超類的指定初始化方法。它還應(yīng)該重寫所有其他繼承的初始化方法,并讓它們以合理的方式運(yùn)行。(有關(guān)指定初始值設(shè)定項(xiàng)的討論,請參閱Object Initialization。)在類集群中,抽象超類的指定初始值設(shè)定項(xiàng)始終為init。

真正的子類:一個例子

假設(shè)您要創(chuàng)建一個NSArray名為的子類,該子類MonthArray返回給定其索引位置的月份名稱。但是,MonthArray對象實(shí)際上不會將月份名稱數(shù)組存儲為實(shí)例變量。相反,給定索引位置(objectAtIndex:)返回名稱的方法將返回常量字符串。因此,無論MonthArray應(yīng)用程序中存在多少個對象,都只會分配12個字符串對象。
MonthArray類被聲明為:

#import <foundation/foundation.h>
@interface MonthArray : NSArray
{
}
 
+ monthArray;
- (unsigned)count;
- (id)objectAtIndex:(unsigned)index;
 
@end

請注意,MonthArray該類未聲明init...方法,因?yàn)樗鼪]有要初始化的實(shí)例變量。如上所述,countobjectAtIndex:方法簡單地覆蓋了繼承的原始方法。

MonthArray該類的實(shí)現(xiàn)如下所示:

#import "MonthArray.h"
 
@implementation MonthArray
 
static MonthArray *sharedMonthArray = nil;
static NSString *months[] = { @"January", @"February", @"March",
    @"April", @"May", @"June", @"July", @"August", @"September",
    @"October", @"November", @"December" };
 
+ monthArray
{
    if (!sharedMonthArray) {
        sharedMonthArray = [[MonthArray alloc] init];
    }
    return sharedMonthArray;
}
- (unsigned)count
{
 return 12;
}
 
- objectAtIndex:(unsigned)index
{
    if (index >= [self count])
        [NSException raise:NSRangeException format:@"***%s: index
            (%d) beyond bounds (%d)", sel_getName(_cmd), index,
            [self count] - 1];
    else
        return months[index];
}
@end

因?yàn)?strong>MonthArray重寫了繼承的原始方法,所以它繼承的派生方法可以正常工作而不會被覆蓋。NSArraylastObject,containsObject:,sortedArrayUsingSelector:objectEnumerator,和其他沒有MonthArray對象問題的方法依然照常執(zhí)行。

復(fù)合的對象

通過將私有集合對象嵌入到您自己設(shè)計(jì)的對象中這個方法,可以創(chuàng)建復(fù)合對象。此復(fù)合對象可以依賴于集合對象的基本功能,僅攔截復(fù)合對象希望以某種特定方式處理的消息。此體系結(jié)構(gòu)減少了必須編寫的代碼量,并允許您利用Foundation Framework提供的測試代碼。

image

復(fù)合對象必須聲明自己是集群的抽象超類的子類。作為子類,它必須覆蓋超類的原始方法。它也可以覆蓋派生方法,但這不是必需的,因?yàn)榕缮椒ㄍㄟ^原始方法工作。
NSArraycount方法就是一個例子; 介入對象的覆蓋方法的實(shí)現(xiàn)可以簡單如下:

- (unsigned)count {
    return [embeddedObject count];
}

這樣,您的對象可以將代碼用于其自身的目的,以實(shí)現(xiàn)它覆蓋的任何方法。

復(fù)合的對象:一個例子

為了說明復(fù)合對象的使用,假設(shè)您需要一個可變數(shù)組對象,該對象在允許對數(shù)組內(nèi)容進(jìn)行任何修改之前,不可以根據(jù)某些驗(yàn)證條件去修改確認(rèn)標(biāo)準(zhǔn)。下面的示例描述了一個名為ValidatingArray的類,它包含一個標(biāo)準(zhǔn)的可變數(shù)組對象。ValidatingArray覆蓋在其超類、NSArray以及NSMutableArray中聲明的所有原始方法。它還聲明arrayvalidatingArrayinit方法,可用于創(chuàng)建和初始化實(shí)例:

#import <foundation/foundation.h>
 
@interface ValidatingArray : NSMutableArray
{
    NSMutableArray *embeddedArray;
}
 
+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex:(unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex:(unsigned)index;
- (void)removeObjectAtIndex:(unsigned)index;
 
@end

實(shí)現(xiàn)文件顯示了在ValidatingArray類的init方法中,如何創(chuàng)建嵌入對象并將其分配給embeddedArray變量。僅訪問數(shù)組但不修改其內(nèi)容的消息被轉(zhuǎn)發(fā)到嵌入對象??梢愿膬?nèi)容的消息(這里是偽代碼)進(jìn)行檢查,并且僅在它們通過假設(shè)驗(yàn)證測試時才進(jìn)行轉(zhuǎn)發(fā)。

#import "ValidatingArray.h"
 
@implementation ValidatingArray
 
- init
{
    self = [super init];
    if (self) {
        embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
    }
    return self;
}
 
+ validatingArray
{
    return [[[self alloc] init] autorelease];
}
 
- (unsigned)count
{
    return [embeddedArray count];
}
 
- objectAtIndex:(unsigned)index
{
    return [embeddedArray objectAtIndex:index];
}
 
- (void)addObject:object
{
    if (/* modification is valid */) {
        [embeddedArray addObject:object];
    }
}
 
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
{
    if (/* modification is valid */) {
        [embeddedArray replaceObjectAtIndex:index withObject:object];
    }
}
 
- (void)removeLastObject;
{
    if (/* modification is valid */) {
        [embeddedArray removeLastObject];
    }
}
- (void)insertObject:object atIndex:(unsigned)index;
{
    if (/* modification is valid */) {
        [embeddedArray insertObject:object atIndex:index];
    }
}
- (void)removeObjectAtIndex:(unsigned)index;
{
    if (/* modification is valid */) {
        [embeddedArray removeObjectAtIndex:index];
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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