再次回顾:继承

我们这次一起来回顾一下之前几节课里提到的继承,我们曾在初见 OOP 里用了一个开发手机(系统)的栗子来描述继承这个东西,相信大家还有印象。

继承

那么这节课我们就深入的来了解了解继承这个概念。

这个其实也不难理解,你看,当你的父辈去世,那他们的财富就会由你继承——好吧,这不是一件很值得开心的事情,但这毕竟是事实。

还有,比如说某一家子人父亲退休了,他的公司就由儿子掌管,我们说这是“子承父业”。

最后说回到 Swift 里边,就是子类继承了父类的成员。这里我们讲“成员”,就是指类里边的属性和方法。

举个栗子吧:这还是一个手机的类

我们写子类来继承父类:

对于父类 CellPhone 来说,它比较抽象,怎么个抽象法呢?任何一部能称得上手机的设备,肯定都具有这样的功能——拨号,打电话,发短信。

对于子类我们确实生产的手机,它就只有最基本的功能,所以 phone 类只需要继承自 CellPhone 就好了,它虽然没有写任何的属性和方法,但它天生就有了 CellPhone 里的全部属性和方法。

对于我们的智能手机 smartPhone ,我们增加了发邮件和上网的功能,但没有实现手机的基本功能——可是它继承自 CellPhone,所以它天生就也带有了手机的所有功能。

至于电量和号码这两个属性,两个子类则可以设定它们各自的内容。

设计一个继承树

假设说我们要开发游戏,这个游戏既有武侠也有科技,既有英雄也有美人——所以说肯定是有战争的——有美人嘛。有了战争,就要有兵器,那么我们就创建一个关于武器的继承树。

首先,设计师告诉我们,一共要有以下几种武器:机枪、手枪、步枪、长剑、开山刀、木棍、内力(这个也算?)、铁锤、弩箭、弓箭、暗器……好了,够多了。

第一步,找出具有共同属性和行为的对象

问一问自己:这些武器都有什么共同点?

啊……这太麻烦了,为什么不干脆每一个武器来一个类呢?

——也不是不行,但是这样的话肯定就会有好多重复的代码了,这样会让整个代码行数大大增加,一个是不利于维护,一旦我们要修改这些武器的某些属性呢?挨个改吧!另一个,就是你程序的 bug 数量是和代码行数成正比的……所以我们有必要有责任也有义务保证代码的简洁。

好了,这些武器肯定得有个属性来保存它们的模型,还得有耐久度吧?得有攻击力,有攻击范围,还得有攻击类型,有等级要求……暂时这么多吧

它们的行为,比如说有攻击,防御,警戒……这样,我们就有了三个方法:

这样,我们就能从武器这里继承出各种各样的武器了!来我们算一算,假设设计师要100中特定的武器,难道真的要把上边这样的代码重复写一百遍吗?!(小学生都不屑如此啦)

好了,现在我们再来看看,冷兵器和热兵器是不同的,它们的攻击方法完全不一样,刀和剑都可以挥砍,但弩和弓就要射击,我们让子类自行覆盖父类的方法——覆盖后边再说,简单的用法你是知道的。

但是还有个问题,手枪机枪都要有子弹有弹夹,这些似乎还是可以提取出来——没错,在众多子类里边,寻找更多可以抽象化的方法是个不错的选择,我们来设计另外一个武器的子类但又是枪支的父类:

这样,我们用枪械这个类去继承出一个子类的话,那它就有了弹药和弹夹,还有属于枪械的攻击防御方法。

完成类的继承树

按照上边的设计思路,我们现在来完成这个继承树:

类的继承树
类的继承树

等等,到底调用哪个方法?

现在我们有了继承树,也按照继承树继承出了最终的实例……可是这个时候怎么确定调用的方法在哪里呢?

首先,既然我们有了继承树,那么编译器肯定也能够知道,那就是说肯定会找到你要调用的方法。

其次,一般我们按照一个递归的方式来查询,从这个继承树最底下开始(也就是最具体的对象开始),一层一层往上查找,知道找到一个对应的——也就是说最接近对象的那个方法就是我们要调用的方法。

比如我们创建了一个手枪的实例,手枪里并没有攻击的方法,我们调用攻击,编译器就会网上查找,找到了父类里重写了的攻击方法——而不是找到最顶层的最原始的攻击方法。

“再次回顾:继承”的一个回复

发表评论

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