我们这次一起来回顾一下之前几节课里提到的继承,我们曾在初见 OOP 里用了一个开发手机(系统)的栗子来描述继承这个东西,相信大家还有印象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class CellPhone { func call() { print("Call someone!") } func powerUp () { print("Welcome!") } func powerDown() { print("Goodbye~") } func sendMessage() { print("sent a Message!") } } |
继承
那么这节课我们就深入的来了解了解继承这个概念。
这个其实也不难理解,你看,当你的父辈去世,那他们的财富就会由你继承——好吧,这不是一件很值得开心的事情,但这毕竟是事实。
还有,比如说某一家子人父亲退休了,他的公司就由儿子掌管,我们说这是“子承父业”。
最后说回到 Swift 里边,就是子类继承了父类的成员。这里我们讲“成员”,就是指类里边的属性和方法。
举个栗子吧:这还是一个手机的类
1 2 3 4 5 6 7 8 9 10 11 12 |
class CellPhone { var number = 0 var battery = 100 func call() { } func message() { } } |
我们写子类来继承父类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Phone:CellPhone { } class SmartPhone:CellPhone { func eMail() { } func internet() { } } |
对于父类 CellPhone 来说,它比较抽象,怎么个抽象法呢?任何一部能称得上手机的设备,肯定都具有这样的功能——拨号,打电话,发短信。
对于子类我们确实生产的手机,它就只有最基本的功能,所以 phone 类只需要继承自 CellPhone 就好了,它虽然没有写任何的属性和方法,但它天生就有了 CellPhone 里的全部属性和方法。
对于我们的智能手机 smartPhone ,我们增加了发邮件和上网的功能,但没有实现手机的基本功能——可是它继承自 CellPhone,所以它天生就也带有了手机的所有功能。
至于电量和号码这两个属性,两个子类则可以设定它们各自的内容。
设计一个继承树
假设说我们要开发游戏,这个游戏既有武侠也有科技,既有英雄也有美人——所以说肯定是有战争的——有美人嘛。有了战争,就要有兵器,那么我们就创建一个关于武器的继承树。
首先,设计师告诉我们,一共要有以下几种武器:机枪、手枪、步枪、长剑、开山刀、木棍、内力(这个也算?)、铁锤、弩箭、弓箭、暗器……好了,够多了。
第一步,找出具有共同属性和行为的对象
问一问自己:这些武器都有什么共同点?
啊……这太麻烦了,为什么不干脆每一个武器来一个类呢?
——也不是不行,但是这样的话肯定就会有好多重复的代码了,这样会让整个代码行数大大增加,一个是不利于维护,一旦我们要修改这些武器的某些属性呢?挨个改吧!另一个,就是你程序的 bug 数量是和代码行数成正比的……所以我们有必要有责任也有义务保证代码的简洁。
好了,这些武器肯定得有个属性来保存它们的模型,还得有耐久度吧?得有攻击力,有攻击范围,还得有攻击类型,有等级要求……暂时这么多吧
它们的行为,比如说有攻击,防御,警戒……这样,我们就有了三个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class 武器 { var 模型 = "" var 耐久度 = 0.0 var 攻击力 = 0.0 var 等级需求 = 0 var 攻击类型 = 0 func 攻击() {} func 防御 () {} func 警戒() {} } |
这样,我们就能从武器这里继承出各种各样的武器了!来我们算一算,假设设计师要100中特定的武器,难道真的要把上边这样的代码重复写一百遍吗?!(小学生都不屑如此啦)
好了,现在我们再来看看,冷兵器和热兵器是不同的,它们的攻击方法完全不一样,刀和剑都可以挥砍,但弩和弓就要射击,我们让子类自行覆盖父类的方法——覆盖后边再说,简单的用法你是知道的。
但是还有个问题,手枪机枪都要有子弹有弹夹,这些似乎还是可以提取出来——没错,在众多子类里边,寻找更多可以抽象化的方法是个不错的选择,我们来设计另外一个武器的子类但又是枪支的父类:
1 2 3 4 5 6 7 |
class 枪械:武器 { var 弹药 = 0 var 弹夹 = 0 override func 攻击() { } override func 防御() { } } |
这样,我们用枪械这个类去继承出一个子类的话,那它就有了弹药和弹夹,还有属于枪械的攻击防御方法。
完成类的继承树
按照上边的设计思路,我们现在来完成这个继承树:
等等,到底调用哪个方法?
现在我们有了继承树,也按照继承树继承出了最终的实例……可是这个时候怎么确定调用的方法在哪里呢?
首先,既然我们有了继承树,那么编译器肯定也能够知道,那就是说肯定会找到你要调用的方法。
其次,一般我们按照一个递归的方式来查询,从这个继承树最底下开始(也就是最具体的对象开始),一层一层往上查找,知道找到一个对应的——也就是说最接近对象的那个方法就是我们要调用的方法。
比如我们创建了一个手枪的实例,手枪里并没有攻击的方法,我们调用攻击,编译器就会网上查找,找到了父类里重写了的攻击方法——而不是找到最顶层的最原始的攻击方法。
转载请保留出处和原文链接:https://www.logcg.com/archives/1118.html
Comments