iOS中子類的理解

這篇文章跟我以往的文章有點不一樣抑堡。它主要是一些思想與模式的匯集站玄,而不是一篇指南按摘。下面我所寫的模式幾乎全都來之不易包券,都是我犯了錯之后才學到的。我并不認為自己是子類方面的權威炫贤,但我確實想把我學到的一些東西分享出來溅固。別把本文當做權威指南,它只是一些例子的匯集兰珍。

在被問到 OOP(面向對象編程)的時候侍郭,Alan Kay(OOP 的發(fā)明人)寫到:

它跟類無關,但跟消息有關。

然而亮元,很多人的關注點仍然還在類層次上猛计。在本文中,我們會看幾個我們可能會把注意力放在創(chuàng)建復雜的類結構上的例子爆捞,并給出更有用的替代方案奉瘤。根據(jù)經(jīng)驗,這樣會讓代碼更簡單煮甥,更易維護盗温。

何時用子類

首先,我們討論幾種使用子類比較合適的場景成肘。

  • 如果你要寫一個自定義布局的 UITableViewCell 卖局,那就創(chuàng)建一個子類。這同樣適用于幾乎每個視圖艇劫。一旦你開始布局吼驶,把這塊代碼放入子類就更合理一些,不光代碼得到了更好的封裝店煞,你也能得到一個可在工程之間重用的組件蟹演。

  • 假設你的代碼是針對多平臺多版本的,并且你需要針對每個平臺每個版本寫一些代碼顷蟀。這時候更合理的做法可能是創(chuàng)建一個 OBJDevice 類酒请,讓一些子類如 OBJIPhoneDevice 和 OBJIPadDevice ,甚至更深層的子類如 OBJIPhone5Device 來繼承鸣个,并讓這些子類重寫特定的方法羞反。例如,你的 OBJDevice 類可能包含了函數(shù) applyRoundedCornersToView:withRadius 囤萤,它有一個默認的實現(xiàn)昼窗,但是也能被特定的子類重寫。

  • 另一個子類化可能很有用的場景是模型對象(model object)涛舍。絕大多數(shù)情況下澄惊,我的模型對象繼承自一個實現(xiàn)了 isEqual: 、 hash 富雅、 copyWithZone: 和 description 等方法的類掸驱。這些方法只被實現(xiàn)一次,并且迭代循環(huán)遍歷所有屬性没佑,所以極不容易出錯毕贼。(如果你也想找一個這樣的基類,可以考慮使用 Mantle 蛤奢,它就是這么做的鬼癣,并且做得更多陶贼。)

何時不使用子類

在以往工作過的很多工程中,我見到過很多繼承層次很深的子類扣溺。當我也這么干的時候骇窍,總會感到內疚。除非繼承的層次非常淺锥余,否則你會很快發(fā)現(xiàn)它的局限性腹纳。

幸運的是,如果你發(fā)現(xiàn)自己正在使用深層次的繼承驱犹,還有很多替代方案可選嘲恍。在下面的章節(jié)中,我們會逐個進行更詳細地描述雄驹。如果你的子類只是使用相同的接口佃牛,協(xié)議會是個非常好的替代方案。如果你知道某個對象需要大量的修改医舆,你可能會使用代理來動態(tài)改變和配置它俘侠。當你想給已有對象增加一些簡單功能時,類別可能是個選擇蔬将。當你有一堆重寫了相同方法的子類時爷速,你可以使用配置對象(configuration object)來代替。最后霞怀,當你想重用某些功能時惫东,組合多個對象而不是擴展它們可能會更好。

替代方案:協(xié)議(Protocols)

很多時候毙石,使用子類的原因是你想保證某個對象可以響應某些消息廉沮。假設在 app 里你有一個播放器對象,它可以播放視頻⌒炀兀現(xiàn)在你想添加對 YouTube 的支持滞时,使用相同的接口,但是具體實現(xiàn)不同滤灯。你可以使像這樣用子類來實現(xiàn):

@class Player : NSObject

- (void)play;
- (void)pause;

@end


@class YouTubePlayer : Player

@end

事實上可能這兩個類并沒有太多共用的代碼坪稽,它們只不過具有相同的接口。如果這樣的話力喷,使用協(xié)議可能會是更好的方案刽漂⊙菅担可以這樣用協(xié)議來寫你的代碼:

@protocol VideoPlayer <NSObject>

- (void)play;
- (void)pause;

@end


@class Player : NSObject <VideoPlayer>

@end


@class YouTubePlayer : NSObject <VideoPlayer>

@end

這樣弟孟,YouTubePlayer 類就不必知道 Player 類的內部實現(xiàn)了。

替代方案:代理(Delegation)

再一次假設你有一個像上面例子中的 Player 類⊙颍現(xiàn)在拂募,你想在開始播放的時候在某個地方執(zhí)行一個自定義的函數(shù)庭猩。這么做相對容易一些:創(chuàng)建一個自定義的子類,重寫 play 方法陈症,調用 [super play ]蔼水,然后開始做你自定義的工作。這么做是一種方法录肯。另外一種方法是趴腋,改動你的 Player 對象,然后給它設置一個代理论咏。如下:

@class Player;

@protocol PlayerDelegate

- (void)playerDidStartPlaying:(Player *)player;

@end


@class Player : NSObject

@property (nonatomic,weak) id<PlayerDelegate> delegate;

- (void)play;
- (void)pause;

@end

現(xiàn)在优炬,在播放器的 play 方法里,就可以給代理發(fā)送 playerDidStartPlaying: 消息了厅贪。這個 Player 類的任何使用者都可以僅僅實現(xiàn)這個代理協(xié)議蠢护,而不用繼承該該類, Player 類也能夠保持通用性养涮。這是個強大有效的技術葵硕,蘋果在自己的框架里大量地使用它。你想想像 UITextField 這樣的類贯吓,還有 NSLayoutManager懈凹。有時候你還會想把幾個不同的方法打包分組到幾個單獨的協(xié)議里,比如 UITableView—— 它不僅有一個代理(delegate)宣决,還有一個數(shù)據(jù)源(dataSource)蘸劈。

替代方案:類別(Categories)

有時候,你可能會想給一個對象增加一點點額外的功能尊沸。比如你想給 NSArray 增加一個方法 arrayByRemovingFirstObject威沫。不用子類,你可以把這個函數(shù)放到一個類別里洼专。像這樣:

@interface NSArray (OBJExtras)

- (void)obj_arrayByRemovingFirstObject;

@end

在用類別擴展一個不是你自己的類的時候棒掠,在方法前添加前綴是個比較好的習慣做法。如果不這么做屁商,有可能別人也用類別對此類添加了相同名字的函數(shù)烟很。那時候程序的行為可能跟你想要的并不一樣,未預期的事情可能會發(fā)生蜡镶。

使用類別還有另外一個風險雾袱,那就是,到最后你可能會使用一大堆的類別官还,連你自己都會失去對代碼全局的認識芹橡。假如那樣的話,創(chuàng)建自定義的類可能更簡單一些望伦。

替代方案:配置對象(Configuration Objects)

在我經(jīng)常會犯的錯誤中(現(xiàn)在很快就能發(fā)現(xiàn)了)林说,其中有一條是:使用一個含有幾個抽象方法的類并讓很多子類來重寫某個方法煎殷。例如,在一個幻燈片應用里腿箩,你有一個主題類 Theme 豪直,它有幾個屬性,比如 backgroundColor 和 font 珠移,還有一些在一張幻燈片上如何布局的邏輯函數(shù)弓乙。

然后,對每種主題钧惧,你都創(chuàng)建一個 Theme 的子類唆貌,重寫某個函數(shù)(例如 setup )并配置其屬性。直接使用父類對此做不了什么事垢乙。在這種情況下锨咙,你可以使用配置對象來讓代碼更簡單些。你可以把共有的邏輯(比如幻燈片布局)放在 Theme 類中追逮,把屬性的配置放到較簡單的對象中酪刀,這些對象中只含有這些屬性。

例如钮孵,類 ThemeConfiguration 具有 backgroundColor 和 font 屬性骂倘,而類 Theme 在其初始化函數(shù)中獲取一個配置類 ThemeConfiguration 的值。

替代方案:組合

組合是代替子類化的最強大有效的方案巴席。如果你想重用已有代碼而不想共享同樣的接口历涝,組合就是你的首選武器。例如漾唉,假設你要設計一個緩存類:

@interface OBJCache : NSObject

- (void)cacheValue:(id)value forKey:(NSString *)key;
- (void)removeCachedValueForKey:(NSString *)key;

@end

簡單點的做法是直接繼承 NSDictionary荧库,通過調用字典的函數(shù)來實現(xiàn)上面的兩個方法。

@interface OBJCache : NSDictionary
但是這么做有幾個弊端赵刑。它本來是應該被詳細實現(xiàn)的分衫,但只是通過字典來實現(xiàn)。現(xiàn)在般此,在任何需要一個 NSDictionary 參數(shù)的時候蚪战,你可以直接提供一個 OBJCache 值。但如果你想把它轉為其它完全不同的東西(例如你自己的庫)铐懊,你就可能需要重構很多代碼了邀桑。

更好的方式是,將這個字典存在一個私有屬性(或者實例變量)中科乎,對外僅僅暴露這兩個 cache 方法”诨現(xiàn)在,當你有了更深入想法的時候喜喂,你可以在靈活地修改其實現(xiàn)瓤摧,而該類的使用者們不用進行重構。

此文章原文鏈接自己的個人博客: www.koalaliu.com ,因簡書平臺規(guī)范性以及用戶量玉吁,搬至簡書照弥。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市进副,隨后出現(xiàn)的幾起案子这揣,更是在濱河造成了極大的恐慌,老刑警劉巖影斑,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件给赞,死亡現(xiàn)場離奇詭異,居然都是意外死亡矫户,警方通過查閱死者的電腦和手機片迅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來皆辽,“玉大人柑蛇,你說我怎么就攤上這事∏疲” “怎么了耻台?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長空另。 經(jīng)常有香客問我盆耽,道長,這世上最難降的妖魔是什么扼菠? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任摄杂,我火速辦了婚禮,結果婚禮上循榆,老公的妹妹穿的比我還像新娘匙姜。我一直安慰自己,他們只是感情好冯痢,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布氮昧。 她就那樣靜靜地躺著,像睡著了一般浦楣。 火紅的嫁衣襯著肌膚如雪袖肥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天振劳,我揣著相機與錄音椎组,去河邊找鬼。 笑死历恐,一個胖子當著我的面吹牛寸癌,可吹牛的內容都是我干的专筷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蒸苇,長吁一口氣:“原來是場噩夢啊……” “哼磷蛹!你這毒婦竟也來了?” 一聲冷哼從身側響起溪烤,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤味咳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后檬嘀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體槽驶,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年鸳兽,在試婚紗的時候發(fā)現(xiàn)自己被綠了掂铐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡揍异,死狀恐怖堡纬,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情蒿秦,我是刑警寧澤烤镐,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站棍鳖,受9級特大地震影響炮叶,放射性物質發(fā)生泄漏。R本人自食惡果不足惜渡处,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一镜悉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧医瘫,春花似錦侣肄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至僚纷,卻和暖如春矩距,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怖竭。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工锥债, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓哮肚,卻偏偏與公主長得像登夫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子允趟,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評論 25 707
  • 葉是普通城鎮(zhèn)的一個普通的女孩拼窥,像這個城鎮(zhèn)其他的女孩一樣,因為她是家中的老大蹋凝,因為她是一個女孩鲁纠,所以在她下面還有一個...
    Phyllis西閱讀 753評論 1 2
  • 俗話說:“一年四季吃枸杞,人可與天地齊壽”鳍寂。枸杞子是一味很好的滋補品改含,可清肝明目、滋陰潤肺迄汛、滋腎益精捍壤。 ...
    枸杞行業(yè)評論員閱讀 292評論 0 1
  • 有時候,聽到別人的消息就會慌了神鞍爱。他們的經(jīng)歷制造了一個幻象——好像生活有無限種可能鹃觉。世界是五彩繽紛的、花樣百出的睹逃、...
    seasea閱讀 467評論 2 3
  • 正交試驗法是研究多因素盗扇、多水平的一種試驗法,它是利用正交表來對試驗進行設計沉填,通過少數(shù)的試驗替代全面試驗疗隶,根據(jù)正交表...
    CC先生之簡書閱讀 5,060評論 0 1