- 論壇徽章:
- 2
|
![]()
鴨子類型是我目前在Ruby語言里最喜歡的一個“功能特征”,主要是因為它能讓我們輕松的寫出漂亮的代碼——畢竟,你無需再擔(dān)心類型:你可以把精力全部集中到你想發(fā)送的消息上,以及你需要打交道的對象能發(fā)揮的功能上。
![]()
我第一次接觸Ruby時就知道它是一種“鴨子類型語言”,但我的靜態(tài)編譯型語言的背景知識妨礙了我真正理解鴨子類型的真正含義。理論很簡單:如果你設(shè)計一個方法,它需要一個‘鴨子’參數(shù),那么你呼叫一聲“嘎嘎”,任何以“嘎嘎”回應(yīng)你的對象都可以傳入這個方法——這個對象究竟是什么類型并不重要。很顯然,你可以得出這樣的結(jié)論,如果你寫出一個Dog類,它實現(xiàn)了一個叫“嘎嘎”的方法(很奇怪的狗),那么,你可以把這個狗傳入上面的那個方法,一點問題都沒有。非?岚桑喿宇愋偷膹(qiáng)大功能震撼了我,我認(rèn)識到,它在各種對象間打通了一條重要的溝通途徑,強(qiáng)化了API的能力,減少了代碼中的干擾。為了說明這些,讓我來展示一些Ruby標(biāo)準(zhǔn)庫中的幾個例子。
File.open
File.open(“path/to/file&rdquo 最常見的讀取文件的方法:你傳入path,這個方法會返回一個能讀取文件的對象。你是否注意到,我加粗強(qiáng)調(diào)了“path”這個詞。這是特意的——這個‘open’函數(shù)實際可以接受任何可以扮演路徑角色的東西,并不僅僅指路徑字符串。這區(qū)別有些微妙,但你會發(fā)現(xiàn)我們可以把代碼這樣寫:
class VimConfig
# ... behavior ... #
def to_path
"~/.vimrc"
end
end
config = VimConfig.new
config_file = File.open config
很帥,不是嗎?Ruby的File API在使用它的參數(shù)前會進(jìn)行轉(zhuǎn)化,轉(zhuǎn)化的一種途徑是通過‘to_path’方法。如果你感到奇怪,下面是實現(xiàn)它的C語言代碼(‘rb_f_open’ 調(diào)用 ‘FilePathValue’,后者最終調(diào)用 ‘rb_get_path_check_to_string&rsquo ):
static VALUE
rb_f_open(int argc, VALUE *argv)
{
ID to_open = 0;
int redirect = FALSE;
if (argc >= 1) {
CONST_ID(to_open, "to_open" ;
if (rb_respond_to(argv[0], to_open)) {
redirect = TRUE;
}
else {
VALUE tmp = argv[0];
FilePathValue(tmp);
if (NIL_P(tmp)) {
redirect = TRUE;
}
else {
VALUE cmd = check_pipe_command(tmp);
if (!NIL_P(cmd)) {
argv[0] = cmd;
return rb_io_s_popen(argc, argv, rb_cIO);
}
}
}
}
if (redirect) {
VALUE io = rb_funcall2(argv[0], to_open, argc-1, argv+1);
if (rb_block_given_p()) {
return rb_ensure(rb_yield, io, io_close, io);
}
return io;
}
return rb_io_s_open(argc, argv, rb_cFile);
}
VALUE
rb_get_path_check_to_string(VALUE obj, int level)
{
VALUE tmp;
ID to_path;
if (insecure_obj_p(obj, level)) {
rb_insecure_operation();
}
if (RB_TYPE_P(obj, T_STRING)) {
return obj;
}
CONST_ID(to_path, "to_path" ;
//to_path call!
tmp = rb_check_funcall(obj, to_path, 0, 0);
if (tmp == Qundef) {
tmp = obj;
}
StringValue(tmp);
return tmp;
}
數(shù)組索引
數(shù)組索引(a_array[index])是另外一個很好的例子:它會向索引調(diào)用‘to_int’方法,所以,任何能響應(yīng)to_int方法的對象都可以當(dāng)作索引。這讓我們可以這樣寫:
class PodiumPosition
# .. behavior .. #
def to_int
@race_position
end
end
position = PodiumPosition.new(1)
prizes = [ "orange", "apple", "corn" ]
puts "Congrats, you won #{prizes[position]}"
IO.select
我是通過IO.select API才第一次發(fā)現(xiàn)了Ruby的強(qiáng)大。這個API會調(diào)用系統(tǒng)select(2)函數(shù),接收文件描述符參數(shù),并掛起當(dāng)前的線程,直到有文件可以進(jìn)行讀寫操作。這個Ruby函數(shù)定義如下:
select(read_array
[, write_array
[, error_array
[, timeout]]]) → array or nil
因此,你可以傳入一個數(shù)據(jù)流數(shù)組,而“select”函數(shù)會一直等到流文件準(zhǔn)備好可讀或可寫。問題是,很多數(shù)據(jù)流是存儲在具有各種行為特征的特定對象里的(例如一個執(zhí)行網(wǎng)絡(luò)操作的Connection類),這些對象里的IO接口通常經(jīng)過了二次封裝,外界無法直接訪問。根本不可能通過重構(gòu)內(nèi)核代碼來適應(yīng)‘select’ API。打破它的封裝嗎?很顯然不行!這時‘to_io’方法就成了救星!
class Connection
# .. rest of the class .. #
def accept_connection(io)
@io = io
# new connection code
end
def to_io
@io
end
end
class Reactor
# array_of_connections_to_read is an array of instances of the above Connection class
# array_of_connections_to_write is an array of instances of the above Connection class
def tick
to_read, to_write = IO.select(array_of_connections_to_read, array_of_connections_to_write)
end
end
你可以看到,Ruby的標(biāo)準(zhǔn)庫里到處都是鴨子類型
![]()
.
重構(gòu)
最明顯,也是最值得一提的鴨子類型的好處是,它讓重構(gòu)變得更容易:“用多型替換條件判斷”和 “Replace Type Code with Strategy/State”的重構(gòu)原則,當(dāng)你不需要考慮類型、只關(guān)心行為時,這些都變得極其簡單和容易實現(xiàn)。
鴨子類型的黑暗面
沒有編譯器為你探路是很危險的。專業(yè)的Ruby程序員(1)永遠(yuǎn)不會忘記有責(zé)任測試它們的代碼的各種行為,并且(2)一定寫出整潔的代碼,并及時重構(gòu)。Ruby代碼必須要認(rèn)真寫,否者調(diào)試起來就會是一場噩夢。
同時,動態(tài)語言一般最合適的是開發(fā)小型或中型軟件。我的經(jīng)驗告訴我,當(dāng)系統(tǒng)變得復(fù)雜時,最好把它拆分成小的應(yīng)用,如果是用動態(tài)語言開發(fā)的,那這種做法更加重要——一個reddit的網(wǎng)友說需要在一個10萬行的程序了修改一個函數(shù)的名稱,我只能說,這很難實現(xiàn)。修改公開的接口,這很難很難。有時最好把它標(biāo)注為‘廢棄’就行了。
結(jié)論
動態(tài)語言能漂亮的解決你的問題,但需要有很好的設(shè)計,Ruby的標(biāo)準(zhǔn)庫里鴨子類型為我們提供了方便的途徑。它是一個很好的例子,向我們展示了一個Ruby程序員該如何的編程:按對象的行為——而不是按對象的類型——來接收參數(shù)。
我希望這篇文章給那些仍然不明白像Ruby這樣的語言的強(qiáng)大之處的人帶來新的認(rèn)識。我推薦閱讀下面幾本書來進(jìn)一步的學(xué)習(xí):
[英文原文:Quacking The Dog - Duck typing for happiness]
轉(zhuǎn)自:http://www.aqee.net/quacking-the-dog-duck-typing-for-happiness/
本文來自ChinaUnix新聞頻道,如果查看原文請點:http://news.chinaunix.net/opensource/2013/0823/2907892.shtml
|
|