類中經(jīng)常容易填滿各種方法,而這些方法的代碼則全部堆在一個(gè)巨大的實(shí)現(xiàn)文件里。有時(shí)這么做是合理的房轿,因?yàn)榧幢阃ㄟ^重構(gòu)把這個(gè)類打散,效果也不會(huì)更好所森。在此情況下囱持,可以通過Objective-C的"分類"機(jī)制,把類代碼按邏輯劃入幾個(gè)分區(qū)中必峰,這對(duì)開發(fā)與調(diào)試都有好處洪唐。
比如說,我們把個(gè)人信息建模為類吼蚁。那么這個(gè)類就可能包含下面幾個(gè)方法:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;
- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;
/* Friendship methods */
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
/* Work methods */
- (void)performDaysWork;
- (void)takeVacationFromWork;
/* Play methods */
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
在實(shí)現(xiàn)該類時(shí)凭需,所有方法的代碼可能會(huì)寫在一個(gè)大文件里。如果還向類中繼續(xù)添加方法的話肝匆,那么源代碼文件就會(huì)越來越大粒蜈,變得難于管理。所以說旗国,應(yīng)該把這樣的類分成幾個(gè)不同的部分枯怖。例如,可以用"分類"機(jī)制把剛才的類改寫成下面這樣:
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
@end
@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end
@interface EOCPerson (Work)
- (void)performDaysWork;
- (void)takeVacationFromWork;
@end
@interface EOCPerson (Play)
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
現(xiàn)在能曾,類的實(shí)現(xiàn)代碼按照方法分成了好幾個(gè)部分度硝。所以說,這項(xiàng)語(yǔ)言特性當(dāng)然就叫做"分類"啦寿冕。在本例中蕊程,類的基本要素(諸如屬性與初始化方法等)都聲明在"主實(shí)現(xiàn)"(main implementation)里。執(zhí)行不同類型的操作所用的另外幾套方法則歸入各個(gè)分類中驼唱。
使用分類機(jī)制之后藻茂,依然可以把整個(gè)類都定義在一個(gè)接口文件中,并將其代碼寫在一個(gè)實(shí)現(xiàn)文件里”娲停可是优俘,隨著分類數(shù)量增加,當(dāng)前這份實(shí)現(xiàn)文件很快就膨脹得無法管理了掀序。此時(shí)可以把每個(gè)分類提取到各自的文件中去帆焕。以EOCPerson為例,可以按照其分類拆分成下列幾個(gè)文件:
- EOCPerson + Friendship(.h/.m)
- EOCPerson + Work(.h/.m)
- EOCPerson + Play(.h/.m)
比方說森枪,與交友功能相關(guān)的那個(gè)分類可以這樣寫:
// EOCPerson+Friendship.h
#import "EOCPerson.h"
@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end
// EOCPerson+Friendship.m
#import "EOCPerson+Friendship.h"
@implementation EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person {
…
}
- (void)removeFriend:(EOCPerson*)person {
…
}
- (BOOL)isFriendsWith:(EOCPerson*)person {
…
}
@end
通過分類機(jī)制视搏,可以把類代碼分成很多個(gè)易于管理的小塊,以便單獨(dú)檢視县袱。使用分類機(jī)制之后浑娜,如果想用分類中的方法,那么要記得在引入EOCPerson.h時(shí)一并引入分類的頭文件式散。雖然稍微有點(diǎn)麻煩筋遭,不過分類仍然是一種管理代碼的好辦法。
即使類本身不是太大暴拄,我們也可以使用分類機(jī)制將其切割成幾塊漓滔,把相應(yīng)代碼歸入不同的"功能區(qū)"(functional area)中。Cocoa中的NSURLRequest類及其可變版本NSMutableURLRequest類就是這么做的乖篷。這個(gè)類用于執(zhí)行從URL中獲取數(shù)據(jù)的請(qǐng)求响驴,而且通常使用HTTP協(xié)議從因特網(wǎng)中的某個(gè)服務(wù)器上獲取,不過撕蔼,由于該類設(shè)計(jì)得較為通用豁鲤,所以也可以使用其他協(xié)議。與標(biāo)準(zhǔn)的URL請(qǐng)求相比鲸沮,執(zhí)行HTTP請(qǐng)求時(shí)還需要另外一些信息琳骡,例如"HTTP"方法(HTTP method,GET讼溺、POST等)或HTTP頭(HTTP header)楣号。
然而卻不便從NSURLRequest中繼承子類以實(shí)現(xiàn)HTTP協(xié)議的特殊需求,因?yàn)楸绢惏艘惶撞僮鰿FURLRequest數(shù)據(jù)結(jié)構(gòu)所需的C函數(shù)怒坯,所有"HTTP方法"都包含在這個(gè)結(jié)構(gòu)里炫狱。于是,為了擴(kuò)展NSURLRequest類剔猿,把與HTTP有關(guān)的方法歸入名為NSHTTPURLRequest的分類中毕荐,而把與可變版本有關(guān)的方法歸入名為NSMutableHTTPURLRequest的分類中。這樣艳馒,所有底層CFURLRequest函數(shù)就都封裝在同一個(gè)Objective-C類里了,而在這個(gè)類里,與HTTP有關(guān)的方法卻又要單獨(dú)放在一處弄慰,因?yàn)槿羰遣贿@么做的話第美,該類的使用者就會(huì)有疑問,為什么能在使用FTP協(xié)議的request對(duì)象上設(shè)置"HTTP方法"呢陆爽?
之所以要將類代碼打散到分類中還有個(gè)原因什往,就是便于調(diào)試:對(duì)于某個(gè)分類中的所有方法來說,分類名稱都會(huì)出現(xiàn)在其符號(hào)中慌闭。例如别威,"addFriend:"方法的"符號(hào)名"(symbol name)如下:
- [EOCPerson(Friendship) addFriend:]
在調(diào)試器的回溯信息中,會(huì)看到類似下面這樣的內(nèi)容:
frame #2: 0x00001c50 Test '-[EOCPerson(Friendship) addFriend:] + 32 at main.m : 46
根據(jù)回溯信息中的分類名稱驴剔,很容易就能精確定位到類中的方法所屬的功能區(qū)省古,這對(duì)于某些應(yīng)該視為私有的方法來說更是極為有用∩ナВ可以創(chuàng)建名為Private的分類豺妓,把這種方法全都放在里面。這個(gè)分類里的方法一般只會(huì)在類或框架內(nèi)部使用布讹,而無須對(duì)外公布琳拭。這樣一來,類的使用者有時(shí)可能會(huì)在查看回溯信息時(shí)發(fā)現(xiàn)private一詞描验,從而知道不應(yīng)該直接調(diào)用此方法了白嘁。這可算作一種編寫"自我描述式代碼"(self-documenting code)的辦法。
在編寫準(zhǔn)備分享給其他開發(fā)者使用的程序庫(kù)時(shí)膘流,可以考慮創(chuàng)建Private分類絮缅。經(jīng)常會(huì)遇到這樣一些方法: 它們不是公共API的一部分,然而卻非常適合在程序庫(kù)之內(nèi)使用睡扬。此時(shí)應(yīng)該創(chuàng)建Private分類盟蚣,如果程序庫(kù)中的某個(gè)地方要用到這些方法,那就引入此分類的頭文件卖怜。而分類的頭文件并不隨程序庫(kù)一并公開屎开,于是該庫(kù)的使用者也就不知道庫(kù)里面還有這些私有方法了。
要點(diǎn)
- 使用分類機(jī)制把類的實(shí)現(xiàn)代碼劃分成易于管理的小塊
- 將應(yīng)該視為"私有"的方法歸入名叫Private的分類中马靠,以隱藏實(shí)現(xiàn)代碼