對象和屬性
我們建立的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”(天使在針尖上)的問題。這完全是根據個人的需要來決定。