class Person attr_reader :name,:age,:height def initialize(name,age,height) @name,@age,@height = name,age,height end def inspect "#@name #@age #@height" end end
在ruby中任何一個(gè)類都可以隨時(shí)打開(kāi)的,這樣可以寫出像2.days_ago這樣優(yōu)美 的code,我們打開(kāi)Array,并定義一個(gè)sort_by方法: Ruby代碼 class Array def sort_by(sysm) self.sort{|x,y| x.send(sym) <=> y.send(sym)} end end 我們看看運(yùn)行結(jié)果: Ruby代碼 people = [] people << Person.new("Hansel",35,69) people << Person.new("Gretel",32,64) people << Person.new("Ted",36,68) people << Person.new("Alice", 33, 63) p1 = people.sort_by(:name) p2 = people.sort_by(:age) p3 = people.sort_by(:height) p p1 # [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68] p p2 # [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68] p p3 # [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69] 這個(gè)結(jié)果是如何得到的呢? 其實(shí)除了send外還有一個(gè)地方應(yīng)該注意attr_reader,attr_reader相當(dāng)于定義了name, age,heigh三個(gè)方法,而Array里的sort方法只需要提供一個(gè)比較方法: x.send(sym) <=> y.send(sym) 通過(guò)send得到person的屬性值,然后在使用<=>比較 二、定制一個(gè)object << object ruby不僅可以打開(kāi)一個(gè)類,而且可以打開(kāi)一個(gè)對(duì)象,給這個(gè)對(duì)象添加或定制功能,而不影響 其他對(duì)象: Ruby代碼 a = "hello" b = "goodbye" def b.upcase gsub(/(.)(.)/)($1.upcase + $2) end puts a.upcase #HELLO puts b.upcase #GoOdBye 我們發(fā)現(xiàn)b.upcase方法被定制成我們自己的了 如果想給一個(gè)對(duì)象添加或定制多個(gè)功能,我們不想多個(gè)def b.method1 def b.method2這么做 我們可以有更模塊化的方式: Ruby代碼 b = "goodbye" class << b def upcase # create single method gsub(/(.)(.)/) { $1.upcase + $2 } end def upcase! gsub!(/(.)(.)/) { $1.upcase + $2 } end end puts b.upcase # GoOdBye puts b # goodbye b.upcase! puts b # GoOdBye 這個(gè)class被叫做singleton class,因?yàn)檫@個(gè)class是針對(duì)b這個(gè)對(duì)象的。 和設(shè)計(jì)模式singleton object類似,只會(huì)發(fā)生一次的東東我們叫singleton. << self 給你定義的class添加行為 Ruby代碼 class TheClass class << self def hello puts "hello!" end end end TheClass.hello #hello! <<self修改了你定義class的class,這是個(gè)很有用的技術(shù),他可以定義class級(jí)別 的helper方法,然后在這個(gè)class的其他的定義中使用。下面一個(gè)列子定義了訪問(wèn) 函數(shù),我們希望訪問(wèn)的時(shí)候把成員數(shù)據(jù)都轉(zhuǎn)化成string,我們可以通過(guò)這個(gè)技術(shù)來(lái) 定義一個(gè)Class-Level的方法accessor_string: Ruby代碼 class MyClass class << self def accessor_string(*names) names.each do |name| class_eval <<-EOF def #{name} @#{name}.to_s end EOF end end end def initialize @a = [ 1, 2, 3 ] @b = Time.now end accessor_string :a, :b end o = MyClass.new puts o.a # 123 puts o.b # Fri Nov 21 09:50:51 +0800 2008 通過(guò)extend module給你的對(duì)象添加行為,module里面的方法變成了對(duì)象里面的 實(shí)例方法: Ruby代碼 module Quantifier def any? self.each { |x| return true if yield x } false end def all? self.each { |x| return false if not yield x } true end end list = [1, 2, 3, 4, 5] list.extend(Quantifier) flag1 = list.any? {|x| x > 5 } # false flag2 = list.any? {|x| x >= 5 } # true flag3 = list.all? {|x| x <= 10 } # true flag4 = list.all? {|x| x % 2 == 0 } # false 三、創(chuàng)建一個(gè)可參數(shù)化的類: 如果我們要?jiǎng)?chuàng)建很多類,這些類只有類成員的初始值不同,我們很容易想起: Ruby代碼 class IntelligentLife # Wrong way to do this! @@home_planet = nil def IntelligentLife.home_planet @@home_planet end def IntelligentLife.home_planet=(x) @@home_planet = x end #... end class Terran < IntelligentLife @@home_planet = "Earth" #... end class Martian < IntelligentLife @@home_planet = "Mars" #... end 這種方式是錯(cuò)誤的,實(shí)際上Ruby中的類成員不僅在這個(gè)類中被所有對(duì)象共享, 實(shí)際上會(huì)被整個(gè)繼承體系共享,所以我們調(diào)用Terran.home_planet,會(huì)輸出 “Mars”,而我們期望的是Earth 一個(gè)可行的方法: 我們可以通過(guò)class_eval在運(yùn)行時(shí)延遲求值來(lái)達(dá)到目標(biāo): Ruby代碼 class IntelligentLife def IntelligentLife.home_planet class_eval("@@home_planet") end def IntelligentLife.home_planet=(x) class_eval("@@home_planet = #{x}") end #... end class Terran < IntelligentLife @@home_planet = "Earth" #... end class Martian < IntelligentLife @@home_planet = "Mars" #... end puts Terran.home_planet # Earth puts Martian.home_planet # Mars 最好的方法: 我們不使用類變量,而是使用類實(shí)例變量: Ruby代碼 class IntelligentLife class << self attr_accessor :home_planet end #... end class Terran < IntelligentLife self.home_planet = "Earth" #... end class Martian < IntelligentLife self.home_planet = "Mars" #... end puts Terran.home_planet # Earth puts Martian.home_planet # Mars 四、Ruby中的Continuations: Continuations恐怕是Ruby中最難理解的概念了,它可以處理非局部的跳轉(zhuǎn), 它保存了返回地址和執(zhí)行的環(huán)境,和c中的setjmp和longjump類似,但它保存 了更多的信息: axgle舉的曹操的例子很形象,我們拿過(guò)來(lái)看看: 來(lái)自[http://www.javaeye.com/topic/44271] 曹操(caocao)被譽(yù)為“古代輕功最好的人 ”,是因?yàn)椤罢f(shuō)曹操,曹操到”這句名言。 在ruby中,曹操的這種輕功被稱為callcc. Ruby代碼 callcc{|caocao| for say in ["曹操","諸葛亮","周瑜"] caocao.call if say=="曹操" puts say #不會(huì)輸出,因?yàn)椴懿僖呀?jīng)飛出去了 end }#“曹操”飛到這里來(lái)了(跳出了callcc的block,緊跟在這個(gè)block后面,繼續(xù)執(zhí)行下面的ruby代碼) puts "到" callcc里的caocao是個(gè)"延續(xù)"(Continuation)對(duì)象.這個(gè)對(duì)象只有名叫“call"的這樣一個(gè)方法。 當(dāng)執(zhí)行了caocao.call后,caocao就會(huì)飛到callcc的塊(block)后面,讓ruby繼續(xù)執(zhí)行其下面的代碼。 我上面給出的是一個(gè)從塊里頭”飛“到塊外面的例子;下面是Programming Ruby給出的從代碼后面”飛“到代碼前面的例子: Ruby代碼 arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ] callcc{|$cc|}#下面的$cc.call如果被執(zhí)行,就會(huì)飛回到這里(callcc的塊之后)。 puts(message = arr.shift) $cc.call unless message =~ /Max/ 例子大多來(lái)自<<The ruby way>>