什么是POP (Protocol-oriented-programing)
- 在我们平常工作中,经常使用的是OOP(Object-Oriented-Programing),比如C++、OC、JAVA。他们是把一些数据或动作集合当做一个对象来看待,比如树、人、机器等。POP则不同,它从另一个视角看待事物:属性。一个对象拥有的数据属性和动作属性,都可以成为一种协议(protocol),不同的对象间有着共同的协议或相似的协议。
- 实际编码过程中,定义各种协议来规范和拓展类的方式,统称叫做POP。
举个🌰
以前我们可能会这样构建一个类:
对于OOP来说是这样定义的,bird为penguin的父类,子类继承和覆盖了父类的属性。 而在POP中,我们这么干:
明显Flyable属性其实不单独属于鸟类,它同样也是很多其他对象的属性。所以在构造其他对象时,我们可以给他们贴上同样的“标签”,接下来我们试着丰富一下我们的标签。 我们定义一个可以描述速度的协议:
这样,我们的✈️和🐦就拥有速度了~ 然后我们可以用Racer来进行拓展。 拓展一个很简单的功能,我们知道Array是继承Sequence协议的,我们可以在Sequence中加入一个功能,使得快速找出Sequence中速度最快的对象。
这样Array就具有了一个简单的功能,我们可以利用这种思想去构建一个体系,Swift中的Array和Dictionary就是很好的例子。
Pop的简单应用
先来看一个简单的例子,iOS的TableView我们通常这样写:
这看起来有点复杂,夹杂着各种判断和转换。所以我们可以利用Swift的特性和POP方式:
有了这两个协议,我们就可以对TableView的方法进行改造,使用泛型方法:
然后在实现的时候,我们会发现一切变得更好了
这只是个实例,这种思想并不会一定帮助你的代码效率更高,但是合理利用下,会让你的代码可读性变得很强,拓展性变的极高,思路变得清晰。下面我们看看在实际项目中POP的应用。
一个网络层框架
这个是一个项目中实际使用的网络请求框架。在父类的APIManager
中包含了一个指向子类的指针,并且强制子类实现一个APIManagerProtocol
。
父类初始化时要求子类的约束条件。
利用self.child来规范子类的实现,同时通过protocol来获取子类的相关信息,使功能实现聚合在BaseAPI中。比如这个获取完整URLString的方法:
实现后我们会发现,子类实现API会非常清晰和简洁。一个API类的实现,只需要几行代码
在以前的错误码处理中,都是统一在APIManager中
处理。但是后来遇到一个问题,由于请求的服务器不同,导致错误码处理不一致,再看我们之前的实现,就有点代码的坏味道了。分析后发现,错误码其实是和服务器挂钩,真正该如何处理一个数据应该是由该服务器说了算,所以我们给XLServer
这个类加了一个协议:
在API处理结果中,对当前子类的server进行判断,如果有响应协议,则交给相应服务器对象处理。 这些在Objective-C中的实践例子,实际用处是规范了各个类的数据和动作的实现,使得代码结构变得清晰,逻辑严密。
简谈函数式编程
- 在Swift中,函数成为了First class value,使得我们可以传递、使用函数就像使用一个数字、一个字符串一样简单。
- 可以粗略的看待一个函数,就是一个过程。
- 我们可以通过变换函数,创造一个个“小工具”,这些工具可以帮助我们构建复杂的过程。
- 函数方便我们调试和找出bug
一个简单的函数式思想的例子
假如我们写一个战船的例子,我们需要一个方法来定位敌方战船是否在我们的有效射程范围内:
为了简化计算,我们抽取其中的几个方法:
最终这个函数表现成这样,看起来条例清晰,逻辑清楚,但是难免在整个函数构建过程中发现,这个过程包含着一次次重构。
现在我们换个思路,把这一个关键的判断过程看做一个对象:
我们把这个过程定义成一个名为区域的类型。然后我们需要一些工具来生成或者改变区域:
有了这些小工具,我们可以轻松的定义之前的函数
POP、泛型和函数式编程的结合
我们可以定义一个操作过程,传入一个参数产生一个结果。这种基础过程遍布我们的应用中:
这里定义了一个枚举的结果类型,用泛型表示结果中的实际类型。定义的operation表示输入一个E类型的数据,产生一个Result<T>
类型的结果。
然后我们可以定义一个combine函数,用于结合两个operation:
测试一下,a是将字典所有key值取出来编程数组的过程,b是把数组转成可读字符串的过程:
为了方便使用,我们先定义一个类,以后也可以在其中做扩展:
以一个数据请求动作为例,拿到的数据通常是JSON转换成的Dictionary,所以我们定义一个协议:
接着我们定义我们的API动作,动作有两个操作,一个是发起请求获取源数据,一个是转换元数据到我们定义的模型:
假如我们现在需要拉取一个水果的信息,我们可以先定义一个水果的类,使他实现协议:
这样一来,我们不用写其他任何多余代码,就可以利用基础的API类做到数据请求。
总结
- Swift带给我们的新体验是Amazing的,虽然POP或IOP概念很早就有,但是结合泛型和函数式编程,学习它而带给我们的是思维上的进化或者革命,让我们拥有更新的视角。
- 对于iOS而言,实际应用中,OOP和POP混合使用是必不可少的,POP的加入的意义是使代码更规范、灵活,使结构和逻辑更清晰易懂。
- 以上所有代码都是Swift-Only的,所以在以后跨平台上,Swift和POP会更有优势。当然在iOS上OC还是舍弃不掉的,OC的优势是更灵活的Runtime,实际应用中更需各取所长。写Swift代码也尽量避免rewrite OC code。
参考
扩展阅读