iOS开发基础篇

iOS 多态的深入探讨

2017-02-27  本文已影响321人  Cheriez

简述

多态一般都要跟继承结合起来说,其本质是子类通过覆盖或重载,父类的方法,来使得对同一类对象同一方法的调用产生不同的结果。这里需要辨析的地方在:同一类对象指的是继承层级再上一层的对象,更加泛化。

使用多态面临的问题

这四种情况在大多数支持多态的语言里面都没有做很好的原生限制,在程序规模逐渐变大的时候,会给维护代码的程序员带来各种各样的坑。

解决方案

面向接口编程(Interface Oriented Programming, IOP)是解决这类问题比较好的一种思路。

举个例子 ~
当一个对象的主要业务功能是搜索,那么它在整个程序里面扮演的角色是搜索者的角色。在基于搜索派生出的业务中,会做一些跟搜索无关的事情,比如搜索后进行人工加权重排列表,搜索前进行关键词分词(假设分词方案根据不同的派生类而不同)。那么这时候如果采用多态的方案,就是由子类覆重父类关于重排列表的方法,覆重分词方法。如果在编写子类的程序员忘记这些必要的覆重或者覆重了不应该覆重的方法,就会进入上面提到的四个困境。所以这时候需要提供一套接口,规范子类去做覆重,从而避免之前提到的四种困境

我们在search.h中先定义一个SearchManager接口,这个接口里面含有原本需要被覆重的方法, 并声明一个对象实现这个接口方法。

<pre>

import <Foundation/Foundation.h>

@protocol SearchManager <NSObject>

@end

@interface Search : NSObject

@property (nonatomic,weak) id< SearchManager > manager;

在search.m文件中

<pre>

...

if(self. manager && [self. manager respondsToSelector:@selector(resort)]){
    self.manager. resort()  
}

...

</pre>

继承search ,创建一个subSearch对象,在subSearch.m文件中,指定manager为自己,并实现接口方法

<pre>
@interface subSearch ()< SearchManager >

@end

@implementation testTwo

//由于子类被接口要求必须实现split()和resort()方法,因而规避了前文提到的风险,在剥离业务的时候也能非常方便。

什么时候用多态

由于多态和继承紧密地结合在了一起,我们假设父类是架构师去设计,子类由客户程序员去实现,那么这个问题实际上是这样的两个问题:

这本质上需要程序员针对对象建立一个角色的概念,越单纯的角色就越容易维护。还有一个就是区分被覆重的方法是否需要被外界调用的问题:

好了,现在我们回到这一节前面提出的两个问题:何时引入接入点和何时采用覆重。针对第一个问题架构师一定要分清楚角色,在保证角色单纯的情况下可以引入多态。另外一点要考虑被覆重的方法是否需要被外界使用,还是只是父类运行时需要子类通过覆重提供中间数据的。如果是只要子类通过覆重提供中间数据的,一律应当采用IOP而不是多态。

总结

多态在面向对象程序中的应用相当广泛,只要有继承的地方,或多或少都会用到多态。然而多态比起继承来,更容易被不明不白地使用,一切看起来都那么顺其自然。在客户程序员这边,一般是只要多态是可行方案的一种,到最后大部分都会采用多态的方案来解决问题。

然而多态正如它名字中所暗示的,它有非常大的潜在可能引入不属于对象初衷的逻辑,巨大的灵活性也导致客户程序员在面对问题的时候不太愿意采用其他相对更优的方案,比如IOP。在决定是否采用多态时,我们要有一个清晰的角色概念,做好角色细分,不要角色混乱。该是拦截器的,就给他制定一个拦截器接口,由另一个对象(逻辑上的另一个对象,当然也可以是自己)去实现接口里的方法集。不要让一个对象在逻辑上既是拦截器又是业务模块。这样才方便未来的维护。另外也要注意被覆重方法的作用,如果只是单纯为了提供父类所需要的中间数据的,一律都用IOP,这是比直接采用多态更优的方案。

IOP能够带来的好处当然不止文中写到的这些,它在其他场合也有非常好的应用,它最主要的好处就在于分离了定义和实现,并且能够带来更高的灵活性,灵活到既可以对语言过高的自由度有一个限制,也可以灵活到允许同一接口的不同实现能够合理地组合。在架构设计方面是个非常重要的思想。

上一篇 下一篇

猜你喜欢

热点阅读