我们创建的Song对象有一个内部状态(如歌曲title和artist)。这个状态对于其它对象是私有的——其它对象不能访问一个对象的实例变量。一般的,这是件好事。这保证了对象的一致性。
然而,一个完全封闭的对象是没什么用的——你能创建它,却不能使用它。你通常定义一些方法让你调用或者操作对象的状态,使用对象和外部世界进行交互。这些可见的部分叫做属性。对于我们的Song对象,我们要做的第一件事是需要能查看它的title和artist(这样我们可以在歌曲播放时显示它们)和播放时间(我们能把它显示在进度条)。
对象和属性
我们创建的Song对象有一个内部状态(如歌曲title和artist)。这个状态对于其它对象是私有的——其它对象不能访问一个对象的实例变量。一般的,这是件好事。这保证了对象的一致性。
然而,一个完全封闭的对象是没什么用的——你能创建它,却不能使用它。你通常定义一些方法让你调用或者操作对象的状态,使用对象和外部世界进行交互。这些可见的部分叫做属性。对于我们的Song对象,我们要做的第一件事是需要能查看它的title和artist(这样我们可以在歌曲播放时显示它们)和播放时间(我们能把它显示在进度条)。
Code
class Song
def name
@name
end
def artist
@artist
end
def duration
@duration
end
end
song = Song.new("Bicylops", "Fleck", 260)
song.artist -> "Fleck"
song.name -> "Bicylops"
song.duration -> 260
这里我们定义了三个访问器来返回三个实例变量。例如,方法name()返回实例变量@name。因为这是一种普遍的风格,所以Ruby提供了一个快捷的方式:attr_reader为你创建这些访问器方法。
Code
class Song
attr_reader :name, :artist, :duration
end
song = Song.new("Bicylops", "Fleck", 260)
song.artist -> "Fleck"
song.name -> "Bicylops"
song.duration -> 260
这个例子介绍了些新东西。这个结构:artist是一个表达式,它返回一个相当于artist的符号对象。你可以这样想:artist是变量artist的名字,artist是这个变量的值。在这个例子中,我们把访问器命名为name,artist和duration.对应的实例变量@name,@artist和@duration会自动创建。这些访问器方法和我们前面写的是一样的。
可写属性
有时候你需要能通过外部对象来设置属性值。例如,让我们假定与歌曲关联的播放时间初始化时是一个估计值 (可能是从CD或MP3中收集的信息)。我们第一次播放歌曲的时候,我们可以得到它实际的长度,然后我们把新值保存到这个Song对象中。
在如C++和Java语言,你可以使用setter方法实现。
Code
class JavaSong { // Java code
private Duration _duration;
public void setDuration(Duration newDuration) {
_duration = newDuration;
}
}
s = new Song(.);
s.setDuration(length);
在Ruby中,一个对象的属性可以像其它变量般访问。如我们前面的song.name。因此,仿佛很自然的当你给属性设置值的时候会把这些值赋给这些变量。在Ruby中,你通过创建一个在名字后面加上一个等号的方法来做到。这些方法能作为赋值的容器。
Code
class Song
def duration=(new_duration)
@duration = new_duration
end
end
song = Song.new("Bicylops", "Fleck", 260)
song.duration -> 260
song.duration = 257 # set attribute with updated value
song.duration -> 257
这个赋值song.duration = 257会执行歌曲对象中的duration=方法,传入参数257。同样的,Ruby提供了一个快捷的方法用于创建简单的属性设置方法。
Code
class Song
attr_writer :duration
end
song = Song.new("Bicylops", "Fleck", 260)
song.duration = 257
属性的实质
这些属性访问方法不是仅仅简单地封装一个对象的实例变量。例如,你可能想以分为单位获得播放时间而不是以秒为单位。
Code
class Song
def duration_in_minutes
@duration/60.0 # force floating point
end
def duration_in_minutes=(new_duration)
@duration = (new_duration*60).to_i
end
end
song = Song.new("Bicylops", "Fleck", 260)
song.duration_in_minutes -> 4.33333333333333
song.duration_in_minutes = 4.2
song.duration -> 252
这里我们使用属性方法创建一个实质的实例变量。
在外部看来,duration_in_minutes就像其它属性一样。在内部,它没有对应的实例变量。
属性,实例变量和方法
属性的描述可能让你觉得它们和方法没什么区别——为什么我们还要起一个别的名字呢?在某种程度上的确如此。属性就是一个方法。有时候属性只是简单地返回一个实例变量的值。有时候属性返回一个计算结果。再有时候这些名字后面带有等号的奇怪方法用来更新一个对象的状态。因此,问题是什么时候用属性什么时候用方法呢?属性和方法的区别又是什么?最终,这不过是一个“angels on a pinhead”(天使在针尖上)的问题。这完全是根据个人的需要来决定。