objc与鸭子对象(下)

我是前言

这是《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/

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部