国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄 搜尋
Ruby用戶(hù)指南 3、開(kāi)始 4、簡(jiǎn)單的例子 5、字符串 6、正則表達(dá)式 7、數(shù)組 8、回到那些簡(jiǎn)單的例子 9、流程控制 10、迭代器 11、面向?qū)ο笏季S 12、方法 13、類(lèi) 14、繼承 15、重載方法 16、訪問(wèn)控制 17、單態(tài)方法 18、模塊 19、過(guò)程對(duì)象 20、變量 21、全局變量 22、實(shí)變量 23、局部變量 24、類(lèi)常量 25、異常處理:rescue 26、異常處理:ensure 27、存取器 28、對(duì)象的初始化 29、雜項(xiàng) RGSS入門(mén)教程 1、什么是RGSS 2、開(kāi)始:最簡(jiǎn)單的腳本 3、數(shù)據(jù)類(lèi)型:數(shù)字 4、數(shù)據(jù)類(lèi)型:常量與變量 5、數(shù)據(jù)類(lèi)型:字符串 6、控制語(yǔ)句:條件分歧語(yǔ)句 7、控制語(yǔ)句:循環(huán) 8、函數(shù) 9、對(duì)象與類(lèi) 10、顯示圖片 11、數(shù)組 12、哈希表(關(guān)聯(lián)數(shù)組) 13、類(lèi) 14、數(shù)據(jù)庫(kù) 15、游戲?qū)ο?/a> 16、精靈的管理 17、窗口的管理 18、活動(dòng)指令 19、場(chǎng)景類(lèi) Programming Ruby的翻譯 Programming Ruby: The Pragmatic Programmer's Guide 前言 Roadmap Ruby.new 類(lèi),對(duì)象和變量 容器Containers,塊Blocks和迭代Iterators 標(biāo)準(zhǔn)類(lèi)型 深入方法 表達(dá)式Expressions 異常,捕捉和拋出(已經(jīng)開(kāi)始,by jellen) 模塊 基本輸入輸出 線(xiàn)程和進(jìn)程 當(dāng)遭遇挫折 Ruby和它的世界 Ruby和Web開(kāi)發(fā) Ruby Tk Ruby 和微軟的 Windows 擴(kuò)展Ruby Ruby語(yǔ)言 (by jellen) 類(lèi)和對(duì)象 (by jellen) Ruby安全 反射Reflection 內(nèi)建類(lèi)和方法 標(biāo)準(zhǔn)庫(kù) OO設(shè)計(jì) 網(wǎng)絡(luò)和Web庫(kù) Windows支持 內(nèi)嵌文檔 交互式Ruby Shell 支持 Ruby參考手冊(cè) Ruby首頁(yè) 卷首語(yǔ) Ruby的啟動(dòng) 環(huán)境變量 對(duì)象 類(lèi) 執(zhí)行 結(jié)束時(shí)的相關(guān)處理 線(xiàn)程 安全模型 正則表達(dá)式 字句構(gòu)造 程序 變量和常數(shù) 字面值 操作符表達(dá)式 控制結(jié)構(gòu) 方法調(diào)用 類(lèi)/方法的定義 內(nèi)部函數(shù) 內(nèi)部變量 內(nèi)部常數(shù) 內(nèi)部類(lèi)/模塊/異常類(lèi) 附加庫(kù) Ruby變更記錄 ruby 1.6 特性 ruby 1.7 特性 Ruby術(shù)語(yǔ)集 Ruby的運(yùn)行平臺(tái) pack模板字符串 sprintf格式 Marshal格式 Ruby FAQ Ruby的陷阱
文字

類(lèi),對(duì)象和變量



看了前面我們談?wù)摰降囊恍├樱阋苍S會(huì)懷疑ruby的面向?qū)ο筇匦允欠駥賹?shí),這章我們將會(huì)詳細(xì)講述這方面的內(nèi)容。我們將會(huì)探討在ruby中如何創(chuàng)建類(lèi)和對(duì)象,并且討論ruby比其他面向?qū)ο笳Z(yǔ)言的一些更強(qiáng)之處。同時(shí),我們也會(huì)部分實(shí)現(xiàn)我們數(shù)億美元的產(chǎn)品:基于因特網(wǎng)的爵士和布魯斯自動(dòng)點(diǎn)唱機(jī)。

經(jīng)過(guò)幾個(gè)月的工作,我們負(fù)責(zé)的研究人員決定我們的點(diǎn)唱機(jī)學(xué)要歌曲(songs),所以我們要在ruby中建立一個(gè)song類(lèi)來(lái)表示現(xiàn)實(shí)中的歌曲。我們知道歌曲都有一個(gè)名字,演唱者,時(shí)長(zhǎng)等,所以,我們的song對(duì)象也應(yīng)如此。

我們開(kāi)始創(chuàng)建了一個(gè)類(lèi):Song,[前面我們已經(jīng)知道類(lèi)名以大寫(xiě)字母開(kāi)頭,而方法以小寫(xiě)字母開(kāi)頭] 它只含有一個(gè)方法:initialize.

class?Song
??def?initialize(name,?artist,?duration)
????@name?????=?name
????@artist???=?artist
????@duration?=?duration
??end
end

initialize 在Ruby中是一個(gè)特殊方當(dāng)法,當(dāng)你調(diào)用Song.new 來(lái)創(chuàng)建一個(gè)Song 對(duì)象的時(shí)候, Ruby先創(chuàng)建一個(gè)沒(méi)有初始化的對(duì)象,然后調(diào)用它的initialize 方法,把傳給new的參數(shù)再傳給 initialize 這個(gè)方法。這樣,我們就可以編寫(xiě)代碼來(lái)設(shè)置對(duì)象的狀態(tài)了。

對(duì)于類(lèi)Song來(lái)說(shuō),initialize 方法接收3個(gè)參數(shù),這三個(gè)參數(shù)作用域跟方法里的局部變量一樣,所以他們的命名方式更具備變量一樣,以小寫(xiě)字母開(kāi)頭。

每個(gè)對(duì)象代表自己的歌曲,他們有不同的名字,演唱者和時(shí)長(zhǎng)等,也就是我們要把這些東西當(dāng)成對(duì)象里面的實(shí)例變量。在ruby中實(shí)例變量用@開(kāi)頭,比如我們上面的例子,@name,@artist,@duration都是實(shí)例變量。

讓我們看看我們的成果如何:

aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.inspect }} "#<Song:0x401b4924?@duration=260,?@artist=\"Fleck\",?@name=\"Bicylops\">"

它已經(jīng)可以工作了。默認(rèn)的inspect方法可以發(fā)送給任何對(duì)象,并且能得到這個(gè)對(duì)象的id和它的實(shí)例變量。從上面的結(jié)果看到我們正確的設(shè)置了對(duì)象的各個(gè)狀態(tài)。

我們的經(jīng)驗(yàn)告訴我們,在開(kāi)發(fā)過(guò)程中,我們要多次打印Song中的內(nèi)容,等inspect的默認(rèn)格式不能完全滿(mǎn)足我們的要求,幸運(yùn)的是,Ruby有一個(gè)標(biāo)準(zhǔn)的消息to_s,當(dāng)向一個(gè)對(duì)象發(fā)送這個(gè)消息時(shí),將會(huì)返回一個(gè)字符串,比如對(duì)于song來(lái)說(shuō):

aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.to_s }} "#<Song:0x401b499c>"

這樣沒(méi)多少用處,甚至還不如inspect,只有對(duì)象id。但是我們可以重載這個(gè)to_s方法。同時(shí),我們也會(huì)用一點(diǎn)時(shí)間來(lái)說(shuō)說(shuō)Ruby中如何定義一個(gè)類(lèi)。

在Ruby中,類(lèi)永遠(yuǎn)不會(huì)關(guān)閉,你可以一直往里面加入方法,不光是你自己寫(xiě)的類(lèi),系統(tǒng)內(nèi)建的類(lèi)也可以加入的。你需要做的是打開(kāi)一個(gè)類(lèi)的定義,然后就可以加入自己的方法了。

這對(duì)我們來(lái)說(shuō)非常好。在本章以后的例子里,我們只需要添加新的方法,老的方法還繼續(xù)存在,這樣省得我們花費(fèi)多余的時(shí)間去在每個(gè)例子里都重寫(xiě)一遍。盡管我們現(xiàn)在寫(xiě)的代碼比較分散,但最好還是把它們都寫(xiě)到一個(gè)文件中去比較好。

我想已足夠詳細(xì)了,還是回到我們要添加的to_s方法吧。

class?Song
??def?to_s
????"Song:?#{@name}--#{@artist}?(#{@duration})"
??end
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.to_s }} "Song:?Bicylops--Fleck?(260)"

非常好,我們進(jìn)步了不少。但是你也許覺(jué)得被騙了,我們?cè)?jīng)說(shuō)過(guò)Ruby中所有對(duì)象都支持to_s方法,但沒(méi)有說(shuō)怎么支持,答案是繼承。ruby如何決定當(dāng)一個(gè)對(duì)象接受一個(gè)消息后執(zhí)行哪個(gè)方法,在下一節(jié)我們將會(huì)看到。

繼承和消息

繼承使你能夠創(chuàng)建一個(gè)基于一個(gè)類(lèi)的特殊化的類(lèi),比如,我們的自動(dòng)點(diǎn)唱機(jī)里有song這個(gè)類(lèi),但是隨著市場(chǎng)的需求,我們需要增加對(duì)卡拉ok的支持??ɡ璷k也是歌曲的一種,只不過(guò)光有伴奏,沒(méi)有演唱音,但是他們需要歌詞這個(gè)屬性,當(dāng)我們播放卡拉ok時(shí),歌詞還要顯示出來(lái)。

比較好的辦法是定義一個(gè)類(lèi)KaraokeSong,它就是一個(gè)song,但是有一個(gè)歌詞的屬性。

class?KaraokeSong?<?Song
??def?initialize(name,?artist,?duration,?lyrics)
????super(name,?artist,?duration)
????@lyrics?=?lyrics
??end
end

" <?Song" 告訴 ruby karaokeSong 這個(gè)類(lèi)是Song的一個(gè)子類(lèi),Song是karaokeSong的父類(lèi)。先不用管initialize這個(gè)方法,以后我們會(huì)談到。

我們可以創(chuàng)建KaraokeSong 對(duì)象看看它是否能工作(在最后的系統(tǒng)中,lyrics存在另外的對(duì)象里,這個(gè)對(duì)象有文本和時(shí)間信息。為了測(cè)試方便,我們這里只用了字符串。這也是無(wú)類(lèi)型語(yǔ)言的優(yōu)點(diǎn):我們?cè)趫?zhí)行代碼之前不需要對(duì)所有對(duì)象進(jìn)行定義。

aSong?=?KaraokeSong.new("My?Way",?"Sinatra",?225,?"And?now,?the...")
aSong.to_s }} "Song:?My?Way--Sinatra?(225)"

這個(gè)類(lèi)已經(jīng)可以工作了,但是to_s沒(méi)有顯示歌詞信息。

這和ruby如何決定調(diào)用哪個(gè)方法的機(jī)制有關(guān)。當(dāng)ruby看到這個(gè) aSong.to_s方法調(diào)用,它并不需要知道去哪里找to_s這個(gè)方法,而是要在程序運(yùn)行到此的時(shí)候再去調(diào)用這個(gè)函數(shù)。開(kāi)始在aSong里面找,如果這個(gè)類(lèi)里面定義了一個(gè)和發(fā)送給這個(gè)對(duì)象的消息一樣名稱(chēng)的方法的話(huà),就運(yùn)行這個(gè)方法。否則,就會(huì)到這個(gè)類(lèi)的父類(lèi)去找,如果還沒(méi)找到,再到父類(lèi)的父類(lèi)去找。這樣一直找到祖先Object。如果找到最高層還沒(méi)有找到這個(gè)方法,一般會(huì)返回一個(gè)錯(cuò)誤。[實(shí)際上,你可以攔截這個(gè)錯(cuò)誤,你可以在運(yùn)行時(shí)彌補(bǔ)這個(gè)錯(cuò)誤,見(jiàn) Object#method_missing ]

現(xiàn)在再回到我們的例子,我們向aSong發(fā)送了一個(gè)消息to_s,在karaokeSong這個(gè)類(lèi)里,ruby找不到to_s這個(gè)方法,所以,再去karaokeSong的父類(lèi)Song去找。在父類(lèi)里發(fā)現(xiàn)了這個(gè)方法,所以就執(zhí)行這個(gè)方法,所以,它只打印了除了歌詞的信息。類(lèi)Song一點(diǎn)都不知道lyrics的存在。

我們可以在這里實(shí)現(xiàn)這個(gè)方法來(lái)彌補(bǔ)這個(gè)不足,又很多方法可以實(shí)現(xiàn)這個(gè)方法,我們先來(lái)看一下一個(gè)不是很好的例子,從Song的to_s拷貝出來(lái)一些代碼,然后加上lyric信息。

class?KaraokeSong
??#?...
??def?to_s
????"KS:?#{@name}--#{@artist}?(#{@duration})?[#{@lyrics}]"
??end
end
aSong?=?KaraokeSong.new("My?Way",?"Sinatra",?225,?"And?now,?the...")
aSong.to_s }} "KS:?My?Way--Sinatra?(225)?[And?now,?the...]"

我們正確地顯示了@lyrics 這個(gè)實(shí)例變量,但是這樣做直接在子類(lèi)里訪問(wèn)了父類(lèi)的實(shí)例變量,為什么這樣實(shí)現(xiàn)to_s方法不好呢?

這和好的編程風(fēng)格有關(guān)(可以稱(chēng)作decoupling
The answer has to do with good programming style (and something called ). By poking around in our parent's internal state, we're tying ourselves tightly to its implementation. Say we decided to change Song to store the duration in milliseconds. Suddenly, KaraokeSong would start reporting ridiculous values. The idea of a karaoke version of ``My Way'' that lasts for 3750 minutes is just too frightening to consider.

我們需要每個(gè)類(lèi)只操作自己內(nèi)部的狀態(tài),當(dāng)KaraokeSong#to_s 被調(diào)用的時(shí)候,先在KaraokeSong#to_s調(diào)用父類(lèi)的to_s方法,然后在加上lyric信息返回給調(diào)用者。這里需要ruby的關(guān)鍵字"super"。當(dāng)你不帶參數(shù)調(diào)用super時(shí),ruby向父類(lèi)發(fā)送消息,調(diào)用父類(lèi)的同名函數(shù)(即和子類(lèi)同名的函數(shù)),傳遞給當(dāng)前類(lèi)方法的參數(shù)會(huì)默認(rèn)的傳給父類(lèi)。比如改寫(xiě)后如下:

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
aSong?=?KaraokeSong.new("My?Way",?"Sinatra",?225,?"And?now,?the...")
aSong.to_s }} "Song:?My?Way--Sinatra?(225)?[And?now,?the...]"

我們顯示的聲明了KaraokeSong是Song的一個(gè)子類(lèi),但是并沒(méi)有說(shuō)明Song的父類(lèi)。如果定義一個(gè)類(lèi)時(shí)沒(méi)有指定父類(lèi),默認(rèn)為Object為它的父類(lèi)。也就是說(shuō),所有的類(lèi)的祖先都是Object類(lèi),而且Object的實(shí)例方法在子類(lèi)中也是可以訪問(wèn)的。比如to_s是ruby中大概35個(gè)實(shí)例方法之一,這些方法列表后面可以看到。

繼承和 Mixins

像c++這樣的面向?qū)ο笳Z(yǔ)言都支持多重繼承,也就是說(shuō)一個(gè)類(lèi)可以有多個(gè)父類(lèi),從每個(gè)類(lèi)繼承特性。盡管很有效,但它有時(shí)候很危險(xiǎn),有可能產(chǎn)生混亂。

其他一些語(yǔ)言,比如java,支持單繼承,一個(gè)類(lèi)只能有一個(gè)父類(lèi),盡管清洗明了,容易實(shí)現(xiàn),但是也有一些缺點(diǎn),因?yàn)槭聦?shí)上一個(gè)事務(wù)同時(shí)具備很多種事務(wù)的特征。比如一個(gè)球,既是球形的東西,也是能彈跳的東西。

ruby采取了有趣而強(qiáng)大的折中辦法,你能輕松的實(shí)現(xiàn)單繼承和多繼承。一個(gè)ruby只能有一個(gè)直接父類(lèi),是單繼承語(yǔ)言,但是ruby類(lèi)可以包含其他的mixin(mixin可以看作是一個(gè)部分類(lèi)定義a partial class definition)中的一些功能,從而引入附加的功能,以這種方式實(shí)現(xiàn)了多重繼承,并且不會(huì)出現(xiàn)多繼承語(yǔ)言中的問(wèn)題。

上面我們已經(jīng)看到了類(lèi)和方法,下面來(lái)看看對(duì)象,也就是類(lèi)的實(shí)例。

對(duì)象和屬性

Song對(duì)象有一些內(nèi)部屬性,比如名稱(chēng)和演唱者,這些屬性都是私有的,其他對(duì)象都不能直接訪問(wèn)。一般來(lái)說(shuō),這樣是不錯(cuò)的設(shè)計(jì),每個(gè)對(duì)象只負(fù)責(zé)自己的完整性,一致性。

但是,如果把對(duì)象裝飾的這么秘密將會(huì)使這些對(duì)象變得毫無(wú)作用,我們能創(chuàng)建它,但是我們不能修改它的屬性。所以,我們可以定義一些方法,通過(guò)這些方法,外部對(duì)象可以訪問(wèn),修改對(duì)象的屬性。這些從外面看起來(lái)表現(xiàn)叫做屬性(attributes)。

對(duì)于我們Song對(duì)象,我們可能需要訪問(wèn)它的名字和演唱者,以便在播放的時(shí)候打印出來(lái),還有它的時(shí)長(zhǎng)(可以用類(lèi)似進(jìn)度條來(lái)顯示)。

class?Song
??def?name
????@name
??end
??def?artist
????@artist
??end
??def?duration
????@duration
??end
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.artist }} "Fleck"
aSong.name }} "Bicylops"
aSong.duration }} 260

這里,我們定義了三個(gè)訪問(wèn)方法,每個(gè)方法返回一個(gè)實(shí)例屬性。在實(shí)際中,這些操作很普遍,所以ruby提供了一個(gè)方便的方法:用attr_reader,它將為我們自動(dòng)創(chuàng)建訪問(wèn)方法。

class?Song
??attr_reader?:name,?:artist,?:duration
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.artist }} "Fleck"
aSong.name }} "Bicylops"
aSong.duration }} 260

這個(gè)例子引入了一些新東西,比如 ":artist"可以當(dāng)作一個(gè)表達(dá)式,返回一個(gè)指向artist的符號(hào)鏈接。也可以把":artist"當(dāng)作是artist的名字。這個(gè)例子里,我們定義了三個(gè)訪問(wèn)方法: name, artist, duration。而實(shí)例變量@name, @artist, @duration會(huì)自動(dòng)創(chuàng)建。這樣定義訪問(wèn)方法和我們上面寫(xiě)的一樣。

可寫(xiě)屬性

有時(shí)候需要在外部對(duì)對(duì)象的屬性進(jìn)行修改。比如,一首歌的時(shí)長(zhǎng)這個(gè)屬性可能開(kāi)始的時(shí)候只是一個(gè)估算的值,當(dāng)?shù)谝淮尾シ诺臅r(shí)候,我們知道了它的真正時(shí)長(zhǎng),并且要把它寫(xiě)回到Song這個(gè)對(duì)象。

在c++或者java中,我們可以用setter方法。

class?JavaSong?{?????????????????????//?Java?code
??private?Duration?myDuration;
??public?void?setDuration(Duration?newDuration)?{
????myDuration?=?newDuration;
??}
}
s?=?new?Song(....)
s.setDuration(length)

在Ruby中,可以象其他變量一樣訪問(wèn)屬性,比如上面我們調(diào)用了aSong.name ,所以我們也應(yīng)該像變量一樣給屬性賦值。在ruby中,這樣做就行:

class?Song
??def?duration=(newDuration)
????@duration?=?newDuration
??end
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.duration }} 260
aSong.duration?=?257???#?set?attribute?with?updated?value
aSong.duration }} 257

賦值語(yǔ)句"aSong.duration?=?257"調(diào)用了aSong中的方法duration= 參數(shù)為257 。實(shí)際上,一個(gè)方法名以=結(jié)尾,就像這個(gè)屬性出現(xiàn)左邊的賦值語(yǔ)句一樣。同樣,ruby也為創(chuàng)建可寫(xiě)屬性提供了一個(gè)快捷方式

?

class?Song
??attr_writer?:duration
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.duration?=?257

虛擬屬性

這些屬性訪問(wèn)方法不是對(duì)一個(gè)對(duì)象的實(shí)例變量的包裝,比如,你需要得到以分鐘為單位的時(shí)長(zhǎng),而不是以秒為單位:

class?Song
??def?durationInMinutes
????@duration/60.0???#?force?floating?point
??end
??def?durationInMinutes=(value)
????@duration?=?(value*60).to_i
??end
end
aSong?=?Song.new("Bicylops",?"Fleck",?260)
aSong.durationInMinutes }} 4.333333333
aSong.durationInMinutes?=?4.2
aSong.duration }} 252

這里我們用屬性方法建立了一個(gè)虛擬的實(shí)例變量,對(duì)于外面來(lái)說(shuō)durationInMinutes可以看作和其他一樣的屬性,但實(shí)際上,并沒(méi)有與之對(duì)應(yīng)的實(shí)例變量。

這并不止是有趣而已,在Bertrand Meyer 的杰作《Object-Oriented Software Construction》?中,作者稱(chēng)這叫做統(tǒng)一訪問(wèn)原則(Uniform Access Principle)。通過(guò)把這些實(shí)例變量和他們計(jì)算之后的值隱藏起來(lái),你就可以不用在自己的實(shí)現(xiàn)里來(lái)處理這些問(wèn)題,而且,當(dāng)需要改動(dòng)的時(shí)候,你只需要改動(dòng)一個(gè)文件,而不是很多文件。

類(lèi)變量和類(lèi)方法

到目前為止我們討論的都是實(shí)例變量和實(shí)例方法,這些變量術(shù)語(yǔ)每個(gè)不同的對(duì)象,用方法來(lái)操作,有時(shí)候,類(lèi)也可能需要自己的狀態(tài),所以引入了類(lèi)變量。

Class Variables

一個(gè)類(lèi)變量被所有它的對(duì)象實(shí)例共享,也可以被下面要提到的類(lèi)方法修改。在系統(tǒng)中,類(lèi)變量只有一個(gè)拷貝。類(lèi)變量名以?xún)蓚€(gè)at 即"@@"開(kāi)頭,比如"@@count"。不想全局變量和實(shí)例變量,類(lèi)變量在使用之前必須被初始化。通常初始化只是類(lèi)定一中的一條賦值語(yǔ)句。

比如,在我們的自動(dòng)點(diǎn)唱機(jī)中,我們想記錄一個(gè)指定的歌曲播放過(guò)多少次,這個(gè)次數(shù)應(yīng)該是一個(gè)song實(shí)例變量,每當(dāng)這個(gè)Song被播放,這個(gè)變量都要加1。如果,我們還想計(jì)算所有歌曲總共播放了多少次,我們可以找到所有Song對(duì)象,然后累加他們的播放次數(shù),或者用全局變量,相反,這里我們用了類(lèi)變量。

class?Song
??@@plays?=?0
??def?initialize(name,?artist,?duration)
????@name?????=?name
????@artist???=?artist
????@duration?=?duration
????@plays????=?0
??end
??def?play
????@plays?+=?1
????@@plays?+=?1
????"This??song:?#@plays?plays.?Total?#@@plays?plays."
??end
end

為了調(diào)試方便, Song#play方法返回了一個(gè)字符串,顯示了這首歌播放過(guò)多少次,和所有歌曲的總的播放次數(shù)。

s1?=?Song.new("Song1",?"Artist1",?234)??#?test?songs..
s2?=?Song.new("Song2",?"Artist2",?345)
s1.play }} "This??song:?1?plays.?Total?1?plays."
s2.play }} "This??song:?1?plays.?Total?2?plays."
s1.play }} "This??song:?2?plays.?Total?3?plays."
s1.play }} "This??song:?3?plays.?Total?4?plays."

類(lèi)變量屬于類(lèi)和它的實(shí)例私有,如果你想在外面訪問(wèn)它,需要編寫(xiě)訪問(wèn)方法,既可以是實(shí)例的訪問(wèn)方法,也可以是下面我們要說(shuō)到的類(lèi)的訪問(wèn)方法。

Class Methods

有時(shí)候,一個(gè)類(lèi)需要提供一個(gè)不需要任何類(lèi)實(shí)例就能使用的方法。我們已經(jīng)見(jiàn)過(guò)一個(gè)這樣的方法了,new方法創(chuàng)建了一個(gè)Song對(duì)象,但是它不屬于Song類(lèi)。

aSong?=?Song.new(....)

你將會(huì)發(fā)現(xiàn)類(lèi)方法貫穿于ruby的庫(kù)文件之中。比如,F(xiàn)ile類(lèi)的對(duì)象表示一個(gè)打開(kāi)的文件,但是File也提供了幾個(gè)類(lèi)方法,比如刪除文件,我們不需要打開(kāi)文件,直接調(diào)用 File.delete ,提供要?jiǎng)h除的文件名就行了。

File.delete("doomedFile")

類(lèi)方法和實(shí)例方法定義時(shí)候是不一樣的,類(lèi)方法定義的時(shí)候要加上類(lèi)名:

class?Example

??def?instMeth??????????????#?instance?method ??end

??def?Example.classMeth?????#?class?method ??end

end

我們的自動(dòng)點(diǎn)唱機(jī)是要收錢(qián)的,按歌曲數(shù)量而不是時(shí)間來(lái)收,所以提供長(zhǎng)的歌曲不如提供短的歌曲效益高。我們不希望在SongList中出現(xiàn)太長(zhǎng)的歌曲,所以我們?cè)赟ongList里面定一個(gè)類(lèi)方法,判斷一首歌是否超過(guò)了規(guī)定的長(zhǎng)度。這個(gè)長(zhǎng)度存在一個(gè)常量里面(常量以大寫(xiě)字母開(kāi)頭),并且在類(lèi)體里面初始化這個(gè)常量。

class?SongList
??MaxTime?=?5*60???????????#??5?minutes
??def?SongList.isTooLong(aSong)
????return?aSong.duration?>?MaxTime
??end
end
song1?=?Song.new("Bicylops",?"Fleck",?260)
SongList.isTooLong(song1) }} false
song2?=?Song.new("The?Calling",?"Santana",?468)
SongList.isTooLong(song2) }} true

單例(Singletons)和其他構(gòu)造函數(shù)

有時(shí)候,你想改變?nèi)笔〉膶?duì)象的創(chuàng)建方式,比如,對(duì)于我們的點(diǎn)唱機(jī)系統(tǒng),我們又很多點(diǎn)唱機(jī),遍布全國(guó),我們想盡可能的使他容易維護(hù),所以我們需要記錄點(diǎn)唱機(jī)發(fā)生的所有事情,比如一首歌被播放了,收錢(qián)了等,所以我們需要一個(gè)日志類(lèi)。因?yàn)槲覀兿氚褞捔艚o音樂(lè)數(shù)據(jù),所以日志記錄在本機(jī)。我們想一個(gè)點(diǎn)唱機(jī)系統(tǒng)只有一個(gè)log類(lèi),并被系統(tǒng)中的所有類(lèi)共有使用。

通過(guò)使用單例模式,要想使用這個(gè)log類(lèi),只有一種創(chuàng)建方法:Logger.create,并且確保系統(tǒng)中只有一個(gè)log的實(shí)例存在。

class?Logger
??private_class_method?:new
??@@logger?=?nil
??def?Logger.create
????@@logger?=?new?unless?@@logger
????@@logger
??end
end

我們把logger類(lèi)的new方法設(shè)成了私有的,這樣就不能用Looger.new來(lái)創(chuàng)建logger對(duì)象了。我們提供了一個(gè)類(lèi)方法 Logger.create ,用到了類(lèi)變量 @@logger ,這是一個(gè)指向logger類(lèi)實(shí)例的引用??梢钥吹剑绻麑?shí)例已經(jīng)創(chuàng)建了,這個(gè)方法直接返回已經(jīng)創(chuàng)建的實(shí)例,不會(huì)再創(chuàng)建第二個(gè)。[這里的實(shí)現(xiàn)是非線(xiàn)程安全的,如果有多個(gè)線(xiàn)程來(lái)訪問(wèn)這個(gè)函數(shù),可能會(huì)出產(chǎn)生多個(gè)logger對(duì)象。我們可以用ruby提供的Singleton mixin來(lái)解決,而不必自己處理線(xiàn)程安全問(wèn)題。]我們可以檢查一下這兩個(gè)方法的返回情況。

Logger.create.id }} 537766930
Logger.create.id }} 537766930

用類(lèi)方法來(lái)包裝構(gòu)造函數(shù),也可以讓使用你的類(lèi)的人感到輕松。比如我們的類(lèi)Shape代表一個(gè)多邊形,構(gòu)造函數(shù)接收邊數(shù)和周長(zhǎng):

class?Shape
??def?initialize(numSides,?perimeter)
????#?...
??end
end

但是,多年以后,使用方法變了,現(xiàn)在需要提供shape的名稱(chēng),邊數(shù),和邊長(zhǎng)而不是周長(zhǎng)。而我們只需要加幾個(gè)類(lèi)方法就行了:

class?Shape
??def?Shape.triangle(sideLength)
????Shape.new(3,?sideLength*3)
??end
??def?Shape.square(sideLength)
????Shape.new(4,?sideLength*4)
??end
end

類(lèi)方法還有很多強(qiáng)大有趣的特性,但是目前我們還是要繼續(xù)我們現(xiàn)在的內(nèi)容。

Access Control

我們?cè)O(shè)計(jì)一個(gè)類(lèi)的接口的時(shí)候,一個(gè)重要的問(wèn)題是,我們應(yīng)該向外界暴露多少內(nèi)部實(shí)現(xiàn),外部能訪問(wèn)我們的類(lèi)有多少限制。如果過(guò)多的讓外部訪問(wèn)內(nèi)部的東西,可能增加了耦合,用戶(hù)越來(lái)越依賴(lài)我們的類(lèi)的內(nèi)部實(shí)現(xiàn),而不是邏輯接口。因?yàn)槲覀円淖円粋€(gè)實(shí)例的狀態(tài)需要調(diào)用這個(gè)實(shí)例的相關(guān)方法,控制對(duì)實(shí)例的方法的訪問(wèn),就能避免對(duì)對(duì)象實(shí)例的狀態(tài)的直接修改。Ruby提供了三種保護(hù)層次:
  • Public methods 任何人都可以訪問(wèn),沒(méi)有訪問(wèn)控制。方法默認(rèn)都是public(initialize除外)。
  • Protected methods 可以在本類(lèi)或者子類(lèi)中調(diào)用。訪問(wèn)控制在家族內(nèi)。
  • Private methods 不能用顯示的接收者來(lái)調(diào)用。cannot be called with an explicit receiver. Because you cannot specify an object when using them, private methods can be called only in the defining class and by direct descendents within that same object.

"protected"和"private"兩者的區(qū)別非常微妙,在ruby中,兩者的關(guān)系和在其他語(yǔ)言中是不一樣的。如果一個(gè)方法是protected的,它可以在定義它的實(shí)例或者子類(lèi)的實(shí)例來(lái)調(diào)用。如果一個(gè)方法是"private"的,只可以在這個(gè)方法所處的對(duì)象中被使用,不能直接調(diào)用另一個(gè)對(duì)象的private方法。甚至這個(gè)對(duì)象就是調(diào)用者本身。
Ruby和其他oo語(yǔ)言另一個(gè)重要的不同點(diǎn)在于,ruby動(dòng)態(tài)確定訪問(wèn)控制,在程序運(yùn)行而不是靜止時(shí),只有你運(yùn)行到那一行,才會(huì)去判斷是否出錯(cuò)。

指定訪問(wèn)控制

在一個(gè)類(lèi)或者模塊定義中設(shè)定方法的訪問(wèn)控制層次: public, protected,private。有兩種定義方法。

如果不帶參數(shù)使用 public/protected/private,那么這后面的方法默認(rèn)都是指定的值,比如一行寫(xiě)了private,那么后面的方法默認(rèn)都是private,除非指定了另外的訪問(wèn)控制符。

class?MyClass

??????def?method1????#?default?is?'public' ????????#... ??????end

??protected??????????#?后面方法將是?'protected'

??????def?method2????#?will?be?'protected' ????????#... ??????end

??private????????????#?后面方法將是?'private'

??????def?method3????#?will?be?'private' ????????#... ??????end

??public?????????????#?subsequent?methods?will?be?'public'

??????def?method4????#?and?this?will?be?'public' ????????#... ??????end end

另一種方法,定義方法的時(shí)候不指定訪問(wèn)控制符,而是將相關(guān)的方法列在對(duì)應(yīng)的訪問(wèn)控制后面,比如:

class?MyClass

??def?method1 ??end

??#?...?and?so?on

??public????:method1,?:method4 ??protected?:method2 ??private???:method3 end

initialize方法自動(dòng)聲明為private型。

現(xiàn)在來(lái)看看一個(gè)例子。假如我們有一個(gè)記帳系統(tǒng),每一個(gè)借方(debit)對(duì)應(yīng)一個(gè)貸方(credit),我們要求都必須遵守這個(gè)規(guī)則,所以我們把debit和credit的方法設(shè)成private,提供了一個(gè)外部接口來(lái)處理:

class?Accounts

??private

????def?debit(account,?amount) ??????account.balance?-=?amount ????end ????def?credit(account,?amount) ??????account.balance?+=?amount ????end

??public

????#... ????def?transferToSavings(amount) ??????debit(@checking,?amount) ??????credit(@savings,?amount) ????end ????#... end

Protected access is used when objects need to access the internal state of other objects of the same class. For example, we may want to allow the individual Account objects to compare their raw balances, but may want to hide those balances from the rest of the world (perhaps because we present them in a different form).

class?Account
??attr_reader?:balance???????#?accessor?method?'balance'

??protected?:balance?????????#?and?make?it?protected

??def?greaterBalanceThan(other) ????return?@balance?>?other.balance ??end end

Because the attribute balance is protected, it's available only within Account objects.

變量Variables

變量用來(lái)跟蹤一個(gè)對(duì)象的狀態(tài),是指向其他對(duì)象的一個(gè)引用 。

person?=?"Tim"
person.id }} 537771100
person.type }} String
person }} "Tim"

第一行,我們創(chuàng)建了一個(gè)新字符串對(duì)象"Tim",person是指向這個(gè)字符串對(duì)象的一個(gè)引用,下兩行的測(cè)試語(yǔ)句顯示了這個(gè)對(duì)象的類(lèi)型和id,最后顯示了它的值。

但變量是對(duì)象嗎?

在ruby中,答案是否定的。一個(gè)變量只是指向一個(gè)對(duì)象的引用。這些對(duì)象可能正在某地,比如堆棧中,變量只是指向了這些對(duì)象。
我們?cè)倏纯瓷晕⒇?fù)責(zé)的例子

person1?=?"Tim"
person2?=?person1
person1[0]?=?'J'
person1 }} "Jim"
person2 }} "Jim"

我們改變了person1的第一個(gè)字符,但是第二個(gè)也跟著改了。這是因?yàn)樽兞恐皇侵赶蛞粋€(gè)對(duì)象的引用,而不是對(duì)象本身。賦值語(yǔ)句person2?=?person1 沒(méi)有創(chuàng)建新的對(duì)象,只是person2拷貝了person1的引用而已。所以person1和person2都指向了同一個(gè)對(duì)象。

有時(shí)候賦值語(yǔ)句只是為對(duì)象建立了別名,而潛在的創(chuàng)建了新的變量指向同一個(gè)對(duì)象。這在我們的代碼中可能會(huì)引起問(wèn)題,但并不像你想的那么容易出錯(cuò)。我們可以用String的dup方法,這個(gè)方法會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象,并且內(nèi)容和消息接受者(方法執(zhí)行者)一樣。

person1?=?"Tim"
person2?=?person1.dup
person1[0]?=?"J"
person1 }} "Jim"
person2 }} "Tim"

如果你不想別人修改一個(gè)對(duì)象,也可以?xún)鼋Y(jié)(freezing)這個(gè)對(duì)象。如果你修改一個(gè)被凍結(jié)的對(duì)象,ruby會(huì)拋出一個(gè)TypeError異常。

person1?=?"Tim"
person2?=?person1
person1.freeze???????#?prevent?modifications?to?the?object
person2[0]?=?"J"
produces:
prog.rb:4:in?`=':?can't?modify?frozen?string?(TypeError)
	from?prog.rb:4


Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright ? 2001 by Addison Wesley Longman, Inc. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/)).

Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.

Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.
上一篇: 下一篇: