上節課我們提到了協議,但是只講了它的一種應用方式,這節課我們就來深入地了解一下這個用起來和 class 差不多的協議究竟有什麼高深奧義。
現在,我們要再一次回顧那個可恥的繼承樹:
這裡我們寫了武器……是用來進行攻擊和防守的。那麼,作為一個遊戲,武器的模型不能夠單單只用在這一個地方,不然的話開發的成本就太大了——我們要盡可能的榨乾代碼的價值。
我們與設計師溝通以後,設計師想了想,我們可以給遊戲的角色加入飾品欄——飾品欄本來就有的啦……但是我們可以把一部分武器作為飾品放進去——比如那些冷兵器的模型,縮小一些就好了,還能增加角色的各種屬性——比如大刀增加攻擊力,長劍就增加速度?
嗯,不錯的選擇,那麼,現在這些武器就需要增加這些功能了——該怎麼辦呢?
還好我們有繼承:
好,讓我們看看,怎麼在這個繼承樹上做文章——目前來看我們能想到的升級方法是
- 給“武器”這個父類直接添加飾品的相關代碼,這樣所有的子類就都 OK 啦! ——不過,一個大砲的飾品?還是一個叫做“內力”的飾品?
- 給每個要做成飾品的武器單獨添加方法! ——好吧,多態不復存在了,你看做飾品的團隊不干死你。
- 把“飾品”做成一個抽象的方法,然後要能作為飾品的武器自己去實現,這樣就符合多態了,也不會出現叫做“內力”的飾品了,可是上百個能作為飾品的武器都要去實現一遍好像也有點太過分了!
- 那麼,要不我們再來個抽像類,然後讓所有的子類去繼承一下?繼承兩個父類!
綜上來看,似乎第三個才是最省力的選擇,效率也是最高的,可是,這裡我們會遇到一個嚴重的 bug:
如果一個類可以同時成為兩個父類的子類——也就是它可以繼承多個父類,那麼誰去保證這兩個父類就一定沒有親緣關係呢?一旦它們祖上哪裡有了一點關係,那這就可能成了一個環!
我們把這個拓撲簡化,一個類繼承自兩個父類,而這兩個父類又同時是同一個類的子類——看吧,這裡出現了一個菱形方塊,最終最底層的子類繼承了最頂層父類成員兩遍!
再深入的我們這裡就不探討了,這就是所謂的“致命方塊”,相信我,你不會願意去處理這個問題的——所以,編譯器也不會允許這樣的事情出現。
其實任何子類都只能從一個類繼承而來。
那這個時候可能有人就要問了:我見過,即使默認項目模板的聲明里都是繼承了好幾個類的,比如說:
1 |
class AppDelegate: NSObject, NSApplicationDelegate {...} |
好吧,其實,NSApplicationDelegate 是這樣聲明的:
1 |
public protocol NSApplicationDelegate : NSObjectProtocol { |
它是一個協議。
協議
好了,這一次,我們來重新認識一下協議——其實它蛋生的真正目的就是為了解決我們上文遇到的問題,而上節課之所以用到了協議,其實是一種曲線救國的辦法罷了。
總之,協議就是一個完全抽象的類——這樣的話就不會出現上文中的致命問題——因為子類要繼承了一個協議(這裡說“繼承”已經不合適,應該叫做“遵循”)就必須實現協議裡的所有抽象成員——這樣就避免了編譯器不能確定版本的問題。而且,還保留了多態的機制!
然後,為了使用上的體驗一致性,Swift 的繼承語法同樣也是協議的遵循語法,而且二者可以寫在一起用逗號分隔,看起來就像是普普通通的聲明。
但你要注意,這些逗號分隔的類型裡,只有一個能是類,其他都得是協議。
這樣,我們就可以讓需要做成飾品的武器子類直接去遵循飾品的協議即可!
1 |
class 长剑:武器,饰品 {} |
另外
我們還要說一點,由於接口允許各種遵循——不再限定單一,而且它是從規則上要求完全抽象的,所以它可以跨繼承樹使用:
比如說我還有個“護甲”的繼承樹,那麼護甲中的一些模型也可以做成飾品,同樣也可以遵循“飾品”協議!
本文由 落格博客 原創撰寫:落格博客 » “致命方塊”:多重繼承與協議
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1132.html