繼承允許你創(chuàng)建一個(gè)類,作為另一個(gè)類的精煉(refinement)和特化(specialization)。例如,在我們的自動(dòng)點(diǎn)唱機(jī)系統(tǒng)中,有“歌曲”這一概念,被封裝在Song類中,然后,隨著市場的成長,我們需要提供卡拉OK的支持。一首卡拉OK歌曲和其他歌曲沒什么兩樣(它只是沒有主唱的音軌,對(duì)此我們不必關(guān)心)。不過,它還包括對(duì)于的一套歌詞以及時(shí)間信息。當(dāng)我們的自動(dòng)點(diǎn)唱機(jī)在播放一首卡拉OK歌曲時(shí),歌詞應(yīng)該隨音樂滾動(dòng)顯示在點(diǎn)唱機(jī)前的屏幕上。
解決這個(gè)問題的一種方法是定義一個(gè)新的類KaraokeSong,就是Song加上歌詞。
1
2
3
4
5
6
7
8
9
10
11
|
class KaraokeSong <Song def initialize(name,artist,duration,lyrics) super (name,artist,duration) @lyrics = lyrics end end |
類定義一行中的“< Song”告訴Ruby, KaraokeSong是Song 的子類(subclass).因此,這也意味著Song是KaraokeSong的超類(superclass)
1
2
3
|
song = KaraokeSong. new ( "My Way" , "Sinatra" , 255 , "And now,the ..." ) song.to_s -> *Song:My Way--Sinatra( 225 )* |
調(diào)用to_s方法沒有顯示歌詞
這和我們?cè)谙蛞粋€(gè)對(duì)象發(fā)送消息時(shí),Ruby判定調(diào)用哪個(gè)方法的機(jī)制有關(guān)。在程序代碼的初始解析(parse)期間,當(dāng)Ruby遇到方法調(diào)用song.to_s時(shí),它并不知道從何處找到to_s方法,而是將判定推遲直至程序開始運(yùn)行時(shí)再運(yùn)行。在那時(shí),Ruby查看song所屬的類。如果該類實(shí)現(xiàn)了和消息名稱相同的方法,就運(yùn)行這個(gè)方法。否則,Ruby就查看其父類中的方法,然后是祖父類,凡此以往追溯整個(gè)祖先鏈。如果最終它在祖先類中沒有找到合適的方法,Ruby會(huì)產(chǎn)生一種特殊的行為,通常是導(dǎo)致引發(fā)一個(gè)錯(cuò)誤。
讓我們通過實(shí)現(xiàn)KaraokeSong#to_s來解決這個(gè)問題,你有許多方法可以完成它。讓我們從最槽糕的方法開始,我們將to_s方法從Song類中拷貝出來并添加lyrics信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class KaraokeSong #... def to_s "KS: #@name--#@artist(#@duration){#@lyrics}" end end song = KaraokeSong. new ( "My Way" , "Sinatra" , 225 , "And now,the..." ) song.to_s -> "KS: My Way--Sinatra(225){And now,the...}" |
我們正確地顯示了實(shí)例變量@lyrics的值。但使用這種方法,子類需要直接訪問其祖先的實(shí)例變量。那么為什么這是實(shí)現(xiàn)to_s的一種糟糕方式呢?
答案與良好的編程風(fēng)格有關(guān)(有時(shí)被稱為解耦)。直接戳進(jìn)父類的內(nèi)部結(jié)構(gòu),并且顯示地檢驗(yàn)它的實(shí)例變量,會(huì)使得我們和父類的實(shí)現(xiàn)緊密地綁在一起。
我們通過讓每個(gè)類處理其自身實(shí)現(xiàn)細(xì)節(jié)的方法來解決這個(gè)問題。當(dāng)調(diào)用KaraokeSong#to_s時(shí),我們調(diào)用其父類的to_s方法來得到歌曲的細(xì)節(jié)。然后,將歌詞信息添加上去,并返回結(jié)果。這里使用的技巧是Ruby的關(guān)鍵字super。當(dāng)你調(diào)用super而不使用參數(shù)時(shí),Ruby向當(dāng)前對(duì)象的父類發(fā)送一個(gè)消息,要求它調(diào)用子類中的同名方法。Ruby將我們?cè)日{(diào)用方法時(shí)的參數(shù)傳遞給父類的方法?,F(xiàn)在,我們可以實(shí)現(xiàn)改進(jìn)后新的to_s方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class KaraokeSong <Song #Format ourselves as a string by appending #our lyrics to our parent's to_s value. def to_s super + "{#@lyrics}" end end song = KaraokeSong. new ( "My Way" , "Sinatra" , 225 , "And now,the..." ) song.to_s -> "Song:My Way--Sinatra(225){And now,the...}" |
我們明確地告訴Ruby,KaraokeSong是Song的子類,但是我們并沒有指定Song類本身的父類是什么。如果你在定義一個(gè)類時(shí)沒有指定其父類,Ruby默認(rèn)以O(shè)bject類作為其父類。這意味著所有類的始祖都是Object,并且Object的實(shí)例方法對(duì)Ruby的所有對(duì)象都可用。