Protocols

在 Objective-C 的世界里面經常錯過的一個東西是抽象接口。接口(interface)這個詞通常指一個類的 .h文件,但是它在 Java 程序員眼里有另外的含義: 一系列不依賴具體實現的方法的定義仁连。(譯者注:在OC中唠叛,類的接口對應在.m文件中都會有具體的實現婴梧,但Java中接口更接近于OC中的抽象接口或者說協議(protocol))在 Objective-C 里是通過 protocol 來實現抽象接口的矗积。因為歷史原因蔓倍,protocol (使用方法類似java的接口)并沒有大量地在Objective-C的代碼中使用也沒有在社區(qū)中普及(指的是那種像Java程序員使用接口那樣來使用protocol的方式)悬钳。一個主要原因是大多數的 Apple 開發(fā)的代碼沒有采用這種的方式,而幾乎所有的開發(fā)者都是遵從 Apple 的模式以及指南偶翅。Apple 幾乎只是在委托模式下使用 protocol默勾。但是抽象接口的概念很強大,在計算機科學的歷史中頗有淵源聚谁,沒有理由不在 Objective-C 中使用母剥。這里通過一個具體的例子來解釋 protocol 的強大力量(用作抽象接口):把非常糟糕的設計的架構改造為一個良好的可復用的代碼。這個例子是在實現一個 RSS 閱讀器(它可是經常在技術面試中作為一個測試題呢)形导。要求很簡單:在TableView中展示一個遠程的RSS訂閱环疼。一個幼稚的方法是創(chuàng)建一個 UITableViewController的子類,并且把所有的檢索訂閱數據朵耕,解析以及展示的邏輯放在一起炫隶,或者說是一個 MVC (Massive View Controller)。這可以跑起來阎曹,但是它的設計非常糟糕伪阶,不過它足夠過一些要求不高的面試了。最小的步驟是遵從單一功能原則处嫌,創(chuàng)建至少兩個組成部分來完成這個任務:一個 feed 解析器來解析搜集到的結果一個 feed 閱讀器來顯示結果
這些類的接口可以是這樣的:

@interface ZOCFeedParser : NSObject
@property (nonatomic, weak) id <ZOCFeedParserDelegate> delegate;
@property (nonatomic, strong) NSURL *url;
- (id)initWithURL:(NSURL *)url;
- (BOOL)start;- (void)stop;
@end
@interface ZOCTableViewController : UITableViewController
- (instancetype)initWithFeedParser:(ZOCFeedParser *)feedParser;
@end

ZOCFeedParser用 NSURL 進行初始化栅贴,來獲取 RSS 訂閱(在這之下可能會使用 NSXMLParser 和 NSXMLParserDelegate 創(chuàng)建有意義的數據),ZOCTableViewController會用這個 parser 來進行初始化锰霜。 我們希望它顯示 parser 接受到的值并且我們用下面的 protocol 實現委托:

@protocol ZOCFeedParserDelegate <NSObject>
@optional
- (void)feedParserDidStart:(ZOCFeedParser *)parser;
- (void)feedParser:(ZOCFeedParser *)parser didParseFeedInfo:(ZOCFeedInfoDTO *)info;
- (void)feedParser:(ZOCFeedParser *)parser didParseFeedItem:(ZOCFeedItemDTO *)item;
- (void)feedParserDidFinish:(ZOCFeedParser *)parser;
- (void)feedParser:(ZOCFeedParser *)parser didFailWithError:(NSError *)error;
@end

我要說筹误,這是一個處理RSS業(yè)務的完全合理而恰當的protocol。這個ViewController在Public接口中將遵循這個protocol:

@interface ZOCTableViewController : UITableViewController <ZOCFeedParserDelegate>

最后創(chuàng)建的代碼是這樣子的:

NSURL *feedURL = [NSURL URLWithString:@"http://www.bbc.co.uk/feed.rss"];
ZOCFeedParser *feedParser = [[ZOCFeedParser alloc] initWithURL:feedURL];
ZOCTableViewController *tableViewController = [[ZOCTableViewController alloc] initWithFeedParser:feedParser];
feedParser.delegate = tableViewController;

到目前你可能覺得你的代碼還是不錯的癣缅,但是有多少代碼是可以有效復用的呢厨剪?view controller 只能處理 ZOCFeedParser 類型的對象: 從這點來看我們只是把代碼分離成了兩個組成部分,而沒有做任何其他有價值的事情友存。view controller 的職責應該是“顯示某些東西提供的內容”祷膳,但是如果我們只允許傳遞ZOCFeedParser的話,就不是這樣的了屡立。這就體現了需要傳遞給 view controller 一個更泛型的對象的需求直晨。我們使用 ZOCFeedParserProtocol這個 protocol (在 ZOCFeedParserProtocol.h 文件里面,同時文件里也有ZOCFeedParserDelegate)。

@protocol ZOCFeedParserProtocol <NSObject>
@property (nonatomic, weak) id <ZOCFeedParserDelegate> delegate;
@property (nonatomic, strong) NSURL *url;
- (BOOL)start;
- (void)stop;
@end
@protocol ZOCFeedParserDelegate <NSObject>
@optional
- (void)feedParserDidStart:(id<ZOCFeedParserProtocol>)parser;
- (void)feedParser:(id<ZOCFeedParserProtocol>)parser didParseFeedInfo:(ZOCFeedInfoDTO *)info;
- (void)feedParser:(id<ZOCFeedParserProtocol>)parser didParseFeedItem:(ZOCFeedItemDTO *)item;
- (void)feedParserDidFinish:(id<ZOCFeedParserProtocol>)parser;
- (void)feedParser:(id<ZOCFeedParserProtocol>)parser didFailWithError:(NSError *)error;
@end

注意這個代理 protocol 現在處理響應我們新的 protocol勇皇, 而且 ZOCFeedParser 的接口文件更加精煉了:

@interface ZOCFeedParser : NSObject <ZOCFeedParserProtocol>
- (id)initWithURL:(NSURL *)url;
@end

因為 ZOCFeedParser實現了 ZOCFeedParserProtocol罩句,它需要實現所有的required方法。 從這點來看 viewController能接受任何遵循該協議的對象敛摘,只要確保所有的對象都會響應start和stop方法并通過delegate屬性提供信息(譯者注:因為protocol默認情況下所有的方法定義都是required的)门烂。對指定的對象而言,這就是viewController所要知道的一切,且不需要知道其實現的細節(jié)兄淫。

@interface ZOCTableViewController : UITableViewController <ZOCFeedParserDelegate>
- (instancetype)initWithFeedParser:(id<ZOCFeedParserProtocol>)feedParser;
@end

上面的代碼片段的改變看起來不多屯远,但是有了一個巨大的提升。view controller 將基于協議而不是具體的實現來工作捕虽。這帶來了以下的優(yōu)點:view controller 現在可以接收通過delegate屬性提供信息的任意對象:可以是 RSS 遠程解析器慨丐,或者本地解析器,或是一個讀取其他遠程或者本地數據的服務ZOCFeedParser和 ZOCFeedParserDelegate可以被其他組成部分復用ZOCViewController(UI邏輯部分)可以被復用測試更簡單了泄私,因為可以用 mock 對象來達到 protocol 預期的效果當實現一個protocol 你總應該堅持 里氏替換原則房揭。這個原則是:你應該可以取代任意接口(也就是Objective-C里的"protocol")實現,而不用改變客戶端或者相關實現晌端。此外崩溪,這也意味著protocol不該關心類的實現細節(jié);設計protocol的抽象表述時應非常用心斩松,并且要牢記它和它背后的實現是不相干的伶唯,真正重要的是協議(這個暴露給使用者的抽象表述)。任何在未來可復用的設計惧盹,無形當中可以提高代碼質量乳幸,這也應該一直是程序員的追求。是否這樣設計代碼钧椰,就是大師和菜鳥的區(qū)別粹断。
最后的代碼可以在這里 找到。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末嫡霞,一起剝皮案震驚了整個濱河市瓶埋,隨后出現的幾起案子,更是在濱河造成了極大的恐慌诊沪,老刑警劉巖养筒,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異端姚,居然都是意外死亡晕粪,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門渐裸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巫湘,“玉大人装悲,你說我怎么就攤上這事∩蟹眨” “怎么了诀诊?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阅嘶。 經常有香客問我畏梆,道長,這世上最難降的妖魔是什么奈懒? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮宪巨,結果婚禮上磷杏,老公的妹妹穿的比我還像新娘。我一直安慰自己捏卓,他們只是感情好极祸,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怠晴,像睡著了一般遥金。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒜田,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天稿械,我揣著相機與錄音,去河邊找鬼冲粤。 笑死美莫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的梯捕。 我是一名探鬼主播厢呵,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼傀顾!你這毒婦竟也來了襟铭?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤短曾,失蹤者是張志新(化名)和其女友劉穎寒砖,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體嫉拐,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡入撒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了椭岩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茅逮。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡璃赡,死狀恐怖,靈堂內的尸體忽然破棺而出献雅,到底是詐尸還是另有隱情碉考,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布挺身,位于F島的核電站侯谁,受9級特大地震影響,放射性物質發(fā)生泄漏章钾。R本人自食惡果不足惜墙贱,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贱傀。 院中可真熱鬧惨撇,春花似錦、人聲如沸府寒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽株搔。三九已至剖淀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纤房,已是汗流浹背纵隔。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炮姨,地道東北人巨朦。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像剑令,于是被迫代替她去往敵國和親糊啡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容

  • 在 Objective-C 的世界里面經常錯過的一個東西是抽象接口吁津。接口(interface)這個詞通常指一個類的...
    YangPu閱讀 274評論 0 1
  • 01-禪與 Objective-C 編程藝術之條件語句與命名 02-禪與 Objective-C 編程藝術之類 C...
    7分醉閱讀 329評論 0 1
  • 發(fā)現 關注 消息 iOS 第三方庫棚蓄、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,103評論 4 62
  • 禪與 Objective-C 編程藝術 (Zen and the Art of the Objective-C C...
    GrayLand閱讀 1,626評論 1 10
  • 不知道什么時候碍脏,社會開始覺得窮梭依,就不配享有人的權利。 窮典尾,不應該結婚役拴,禍害了人家女兒 窮,不配生育钾埂,生出來禍害了孩...
    炎術士閱讀 370評論 0 0