“致命方块”:多重继承与协议

上节课我们提到了协议,但是只讲了它的一种应用方式,这节课我们就来深入地了解一下这个用起来和 class 差不多的协议究竟有什么高深奥义。

现在,我们要再一次回顾那个可耻的继承树:

类的继承树
类的继承树

这里我们写了武器……是用来进行攻击和防守的。那么,作为一个游戏,武器的模型不能够单单只用在这一个地方,不然的话开发的成本就太大了——我们要尽可能的榨干代码的价值。

我们与设计师沟通以后,设计师想了想,我们可以给游戏的角色加入饰品栏——饰品栏本来就有的啦……但是我们可以把一部分武器作为饰品放进去——比如那些冷兵器的模型,缩小一些就好了,还能增加角色的各种属性——比如大刀增加攻击力,长剑就增加速度?

嗯,不错的选择,那么,现在这些武器就需要增加这些功能了——该怎么办呢?

还好我们有继承:

好,让我们看看,怎么在这个继承树上做文章——目前来看我们能想到的升级方法是

  • 给“武器”这个父类直接添加饰品的相关代码,这样所有的子类就都 OK 啦! ——不过,一个大炮的饰品?还是一个叫做“内力”的饰品?
  • 给每个要做成饰品的武器单独添加方法! ——好吧,多态不复存在了,你看做饰品的团队不干死你。
  • 把“饰品”做成一个抽象的方法,然后要能作为饰品的武器自己去实现,这样就符合多态了,也不会出现叫做“内力”的饰品了,可是上百个能作为饰品的武器都要去实现一遍好像也有点太过分了!
  • 那么,要不我们再来个抽象类,然后让所有的子类去继承一下?继承两个父类!

综上来看,似乎第三个才是最省力的选择,效率也是最高的,可是,这里我们会遇到一个严重的 bug:

如果一个类可以同时成为两个父类的子类——也就是它可以继承多个父类,那么谁去保证这两个父类就一定没有亲缘关系呢?一旦它们祖上哪里有了一点关系,那这就可能成了一个环!

我们把这个拓扑简化,一个类继承自两个父类,而这两个父类又同时是同一个类的子类——看吧,这里出现了一个菱形方块,最终最底层的子类继承了最顶层父类成员两遍!

再深入的我们这里就不探讨了,这就是所谓的“致命方块”,相信我,你不会愿意去处理这个问题的——所以,编译器也不会允许这样的事情出现。

其实任何子类都只能从一个类继承而来。

那这个时候可能有人就要问了:我见过,即使默认项目模板的声明里都是继承了好几个类的,比如说:

好吧,其实,NSApplicationDelegate 是这样声明的:

它是一个协议。

协议

好了,这一次,我们来重新认识一下协议——其实它蛋生的真正目的就是为了解决我们上文遇到的问题,而上节课之所以用到了协议,其实是一种曲线救国的办法罢了。

总之,协议就是一个完全抽象的类——这样的话就不会出现上文中的致命问题——因为子类要继承了一个协议(这里说“继承”已经不合适,应该叫做“遵循”)就必须实现协议里的所有抽象成员——这样就避免了编译器不能确定版本的问题。而且,还保留了多态的机制!

然后,为了使用上的体验一致性,Swift 的继承语法同样也是协议的遵循语法,而且二者可以写在一起用逗号分隔,看起来就像是普普通通的声明。

但你要注意,这些逗号分隔的类型里,只有一个能是类,其他都得是协议。

这样,我们就可以让需要做成饰品的武器子类直接去遵循饰品的协议即可!

另外

我们还要说一点,由于接口允许各种遵循——不再限定单一,而且它是从规则上要求完全抽象的,所以它可以跨继承树使用:

比如说我还有个“护甲”的继承树,那么护甲中的一些模型也可以做成饰品,同样也可以遵循“饰品”协议!

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注