(这篇内容比较丰富,比较多知识点了.不过也比较容易理解)
始创建了一个类:
Song
class Song
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
end
initialize 在Ruby中是一个特殊方当法,当你调用Song.new 来创建一个Song 对象的时候, Ruby先创建一个没有初始化的对象,然后调用它的initialize 方法,把传给new的参数再传给 initialize 这个方法。
aSong = Song.new("Bicylops", "Fleck", 260) |
aSong.inspect | }} | "#<Song:0x401b4924 @duration=260, @artist=\"Fleck\", @name=\"Bicylops\">" |
默认的inspect方法可以发送给任何对象,并且能得到这个对象的id和它的实例变量.
在Ruby中,类永远不会关闭,你可以一直往里面加入方法,不光是你自己写的类,系统内建的类也可以加入的。你需要做的是打开一个类的定义,然后就可以加入自己的方法了。
Ruby有一个标准的消息to_s,当向一个对象发送这个消息时,将会返回一个字符串,比如对于song来说:
aSong = Song.new("Bicylops", "Fleck", 260) |
aSong.to_s | }} | "#<Song:0x401b499c>" |
我们可以重载这个to_s方法,要添加的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)" |
继承和消息
定义一个类KaraokeSong,它就是一个song,但是有一个歌词的属性。
class KaraokeSong < Song
def initialize(name, artist, duration, lyrics)
super(name, artist, duration)
@lyrics = lyrics
end
end
|
aSong = KaraokeSong.new("My Way", "Sinatra", 225, "And now, the...") |
aSong.to_s |
}} |
"Song: My Way--Sinatra (225)" |
这个类已经可以工作了,但是to_s没有显示歌词信息。
当ruby看到这个 aSong.to_s方法调用,它并不需要知道去哪里找to_s这个方法,而是要在程序运行到此的时候再去调用这个函数。开始在aSong里面找,如果这个类里面定义了一个和发送给这个对象的消息一样名称的方法的话,就运行这个方法。否则,就会到这个类的父类去找,如果还没找到,再到父类的父类去找。这样一直找到祖先Object。如果找到最高层还没有找到这个方法,一般会返回一个错误。
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 这个实例变量,但是这样做直接在子类里访问了父类的实例变量
(问题出现了,怎么解决他们呢?下面就是方法了.)
每个类只操作自己内部的状态,当KaraokeSong#to_s 被调用的时候,先在KaraokeSong#to_s调用父类的to_s方法,然后在加上lyric信息返回给调用者。这里需要ruby的关键字"super"。当你不带参数调用super时,ruby向父类发送消息,调用父类的同名函数(即和子类同名的函数),传递给当前类方法的参数会默认的传给父类。
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...]" |
对象和属性
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 |
定义了三个访问方法,每个方法返回一个实例属性。在实际中,这些操作很普遍,所以ruby提供了一个方便的方法:用attr_reader,它将为我们自动创建访问方法。
(这个感情好啊,不用搞一堆的set和get,直接标准化实现)
class Song |
attr_reader :name, :artist, :duration |
end |
aSong = Song.new("Bicylops", "Fleck", 260) |
aSong.artist |
}} |
"Fleck" |
aSong.name |
}} |
"Bicylops" |
aSong.duration |
}} |
260 |
可写属性
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 |
一个方法名以=结尾,就像这个属性出现左边的赋值语句一样。ruby也为创建可写属性提供了一个快捷方式.
class Song
attr_writer :duration
end
aSong = Song.new("Bicylops", "Fleck", 260)
aSong.duration = 257
这些属性访问方法不是对一个对象的实例变量的包装,比如,你需要得到以分钟为单位的时长,而不是以秒为单位:
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 |
用属性方法建立了一个虚拟的实例变量,对于外面来说durationInMinutes可以看作和其他一样的属性,但实际上,并没有与之对应的实例变量。
Class Variables
一个类变量被所有它的对象实例共享,也可以被下面要提到的类方法修改。在系统中,类变量只有一个拷贝。类变量名以两个at 即"@@"开头,比如"@@count"。不像全局变量和实例变量,类变量在使用之前必须被初始化。通常初始化只是类定一中的一条赋值语句。
(类似于static变量)
我们想记录一个指定的歌曲播放过多少次,这个次数应该是一个song实例变量,每当这个Song被播放,这个变量都要加1。如果,我们还想计算所有歌曲总共播放了多少次,我们用类变量
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
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." |
类变量属于类和它的实例私有,如果你想在外面访问它,需要编写访问方法,既可以是实例的访问方法,也可以是下面我们要说到的类的访问方法。
有时候,一个类需要提供一个不需要任何类实例就能使用的方法。
类方法和实例方法定义时候是不一样的,类方法定义的时候要加上类名:
class Example
def instMeth # instance method
end
def Example.classMeth # class method
end
end
一个点唱机系统只有一个log类,并被系统中的所有类共有使用 通过使用单例模式,要想使用这个log类,只有一种创建方法:Logger.create,并且确保系统中只有一个log的实例存在。 class Logger
private_class_method :new
@@logger = nil
def Logger.create
@@logger = new unless @@logger
@@logger
end
end
Public methods 任何人都可以访问,没有访问控制。方法默认都是public(initialize除外)。 Protected methods 可以在本类或者子类中调用。访问控制在家族内。 Private methods 不能用显示的接收者来调用。ruby动态确定访问控制,在程序运行而不是静止时,只有你运行到那一行,才会去判断是否出错。 如果不带参数使用 public/protected/private,那么这后面的方法默认都是指定的值,比如一行写了private,那么后面的方法默认都是private,除非指定了另外的访问控制符。 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
另一种方法,定义方法的时候不指定访问控制符,而是将相关的方法列在对应的访问控制后面,比如 |
class MyClass
def method1
end
# ... and so on
public :method1, :method4
protected :method2
private :method3
end
变量用来跟踪一个对象的状态,是指向其他对象的一个引用 。
person1 = "Tim" |
person2 = person1 |
|
person1[0] = 'J' |
|
person1 | }} | "Jim" |
person2 | }} | "Jim" |
赋值语句只是为对象建立了别名,而潜在的创建了新的变量指向同一个对象。
可以用String的dup方法,这个方法会创建一个新的字符串对象,并且内容和消息接受者(方法执行者)一样。
person1 = "Tim" |
person2 = person1.dup |
person1[0] = "J" |
person1 | }} | "Jim" |
person2 | }} | "Tim" |
如果你不想别人修改一个对象,也可以冻结(freezing)这个对象。如果你修改一个被冻结的对象,ruby会抛出一个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
|