objc與鴨子物件(下)

發表於2017-01-17

我是前言

這是《objc與鴨子物件》的下半部分,《objc與鴨子物件(上)》中介紹了鴨子型別和它在objc中的實踐,以及一個使用NSProxy實現JSON Entity的鴨子類。下半部分介紹鴨子物件的進階用法,並簡單介紹由鴨子物件思想衍生出的依賴注入,實現一個demo。


被誤解了的物件導向

Smalltalk之父或者說物件導向之父(之一)的Alan Kay曾寫過:

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” – that is what the kernal of Smalltalk/Squeak is all about.

物件導向的思想的核心並不在於object或者class,而在於message,或者說物件和類只是訊息的載體。物件導向思想將程式按功能和邏輯分成了若干個類,每個類包含自己的程式碼、功能實現並提供對外介面,以黑箱模式執行,使得外部無需瞭解內部而協同使用,分解了複雜度並控制在一個個較小規模中,以訊息作為其間所有的協作方式。
回到主題,理解了message才是全部,鴨子物件又可以更近一層,試想下整個程式,每個類除了知道自己的類之外其他類名一無所知,全部通過協議發訊息:


Json Entity的重構

回想上一篇中的JSON Entity類:

幹嘛caller要知道有這麼個Class存在呢?它關心的只是能用哪些message通訊而已。於是把類宣告移動到.m中,簡化成一個C的建立方法(類工廠方法同樣會暴露類名):

如果這個類需要提供其他message介面供caller使用,則:

被註釋掉是因為真實使用場景會造成型別不匹配造成編譯警告,所以caller使用起來:

這樣重構的鴨子物件不僅隱藏了內部實現是個字典的事實,連它究竟是什麼Class都隱藏了,但程式執行並無影響,騙一騙編譯器罷了。不過這個思路的改變確引出另一個技術思路,那就是依賴注入


依賴注入

Dependency Injection,簡稱DI,其實在這個場景下叫動態實現注入更合適。它的思想是將一個“物件”分成三部分,protocolproxyimplementation,試想有兩個協議,他們定義了彼此間該如何傳送message:
1151530583jw1ejqir7ys6zj20lu04ywes
執行時他們都是由proxy物件扮演:
1251530583jw1ejqj0qchl2j20m009eq3k
但DI Proxy並不能響應任何message,真正的實現是動態被“注入”到Proxy中的:
1351530583jw1ejqj6c7uqrj20l60dw75f
由於呼叫層只有協議沒有類名,所以Implement A實現類並不依賴Implement B,就像販毒團伙的兩方只靠小弟來交易,完全不知道幕後大哥是誰,這就是所謂的“面向介面程式設計”吧。

Let’s demo it

重點在實現這個Proxy類,按照剛才重構Json Entity類的思路,標頭檔案定義十分精簡:

既然都叫Proxy了,再不使用NSProxy類都對不起它了。這個類使用一個字典來儲存被注入的實現物件,以及與protocol的對應關係:

實現協議內容:

關鍵步驟還是訊息轉發,非常簡單,把收到的訊息轉發給能處理的implementation物件(如果用NSObject的forwardingTargetForSelector將更加簡單):

有了Proxy類,下面是另外兩個角色的測試程式碼,協議:

實現類(漢字是可以正常編譯執行的- -):

測試程式碼:

這個簡單的demo就完成了。
這個demo的原始碼可以->從這裡下載,have fun.


我是後語

現在有一個完整的依賴注入框架typhoon,感興趣的可以把玩一下。
依賴注入不僅可以解耦依賴關係,也可以更好的Test和Mock,想測試某個物件只需要將實現物件注入成Test物件,想造假資料只需要將response物件替換成一個Mock物件,無需修改呼叫程式碼,天然不刺激~

PS: 實際使用中可不要過度設計哦。。。


Reference

http://c2.com/cgi/wiki?AlanKayOnMessaging
http://www.typhoonframework.org/

相關文章