[Ruby] class 與 instance
在看 Ruby 的 class 與 instance 之前,先來看所謂
prototype-based language 是怎麼樣的東西,
當然是舉大家最耳熟能詳的例子,ECMAScript(即 Javascript)
所謂 prototype-based 的意思是,沒有 class 的概念,所有的一切都是
instance, 產生東西一律使用 clone 的手法,從 prototype clone 出來
如果有耐心慢慢看英文的話,這篇很詳細
http://en.wikipedia.org/wiki/Prototype-based_programming
在 ECMAScript 裡,要這樣操作 prototype:
=begin 例子
// 產生一個 function object, 會輸出「我是 ooo」
function say(){ print('I am ' + this) }
// 產生一個 function object, 拿這當 Duck 的 prototype
function Duck(){}
// 讓 Duck prototype 產生一個成員,也就是讓 say 變成他的 method
Duck.prototype.say = say;
// 定義 toString 讓 say 使用
Duck.prototype.toString = function(){ return 'Duck' }
// 假設現在有一個讓某東西說話的 function
function say_hello(who){ who.say() }
// 於是我們可以這樣呼叫 say_hello
say_hello(new Duck)
=end 例子
new Duck 會去尋找 Duck 的 prototype, 然後 clone 一份該 prototype 後傳回
所以 say_hello 的 who 會是一份 Duck 的複製,執行 say 則會輸出
I am Duck
ok, 回到 Ruby. 雖然說 Ruby 被分類成 class-based, 但事實上,
everything(ok, almost) is an object in Ruby, 就算是 class,
他其實本質上也是某個 instance, 是 Class 的 instance.
class A; end
a = A.new
a 是 A 的 instance, 所以 a 的 class 是 A.
a.class # A
A 是 Class 的 instance, 所以 A 的 class 是 Class
A.class # Class
其實,我覺得可以把這個 Class 視為某種 meta-class, 即 class 的 class,
如果我們要把 A 當嚴格 class 的話。但如果我們依然把 class 當 instance
看的話,當然,Class 本身其實也是一個 instance, 他是他自己的 instance.
Class.class # Class
有趣的是,這樣寫的話:
Class.object_id == Class.class.object_id
答案是:true.
Class.class 傳回來的,其實就是 Class, 也就是,他是他自己的 instance.
換句話說,其實 Class 是所有的 class 的 class,
A.kind_of? Class # true
Class.kind_of? Class # true
A.class # Class
Class.class # Class
A.class.object_id == Class.object_id # true
(btw, 其實更妙的是:
Module.kind_of? Class # true # Module 是 Class 的 instance
Class.kind_of? Module # true # Module 是 Class 的 superclass
Module.new.kind_of? Class # false # Module 的 instance 不是 Class
再加上 Object 會更複雜,可以試著畫畫看物件結構)
但是回想一下,一般我們是怎麼定義 class 的?
class A; end
其實,我個人會說這是一種 syntax sugar, 因為更合於 Ruby object system 的
定義方式,應該是這樣:
A = Class.new
A.send(:define_method,
:say_hello, lambda{ puts "Hello from A's instance." })
a = A.new
a.say_hello # Hello from A.
由於 define_method 是 private 的,所以要用 send 去呼叫
其第一參數是你所要回應的 message symbol, 第二參數是 Proc/Method/Block 都可
將會成為該 method 的 body.
也就是說,其實你寫
class A
def say_hello
puts "Hello from A's instance."
end
end
對於 A 來說,他是先從 Class 產生一個實體(instance)
然後將 A 這個「常數」指向那個實體,再對 A 呼叫 define_method
把 say_hello 變成 symbol, 將 Block 變成該 method 的 body.
哪一個比較容易寫?當然是後者,畢竟那是大家都很習慣的模式,簡潔易懂
所以我會說那種寫法其實在某種程度上來說,是 syntax sugar...
而 Ruby 其實也是用 prototype 建出其 class 體系,
這樣應該算是 prototype-based 還是 class-based, 看倌認為哩?
OK!
回到最早發表這篇文章的動機之來源問題,怎麼刪掉某個 class?
由於以 GC 為記憶體核心模型的程式語言,通常不允許你對某個物件明白刪除
我們能做的只有把他設為 nil, 像是
a = A.new
a = nil # 希望 GC 收回
A 當然也是某個 instance, 只是記得「大寫」開頭的 identifier 是常數吧?
A 正是一個 const pointer 指向某個 instance(A 的 prototype)
A = nil # 希望 GC 收回
這樣就可以很暴力地期望 GC 會回收 A...
我不知道這樣做能不能 work, 只知道肯定會噴 warning, 因為你竄改常數
安全性設高一點的話,這樣做甚至會失敗(因為不能修改常數)
很好!再回想一下,你不能寫 class a; end 因為 class 要大寫開頭(才是常數)
但是你可以這樣寫:
a = Class.new
a.send(:define_method, :say_hello, lambda{...})
a.new.say_hello # ok
a = nil # ok
現在 a 不是常數了,但是他仍然是某個 class!
確實是可以正確地將他指為 nil 了…
可是話說回來,既然都在用 Ruby 了,刪除東西真的是很重要的一件事嗎?
當然不是!記住 scripting 的優勢在哪裡,如果放棄這個優勢,不如去用
system langauge, 如 C++ 之類的會遠比在這邊磨 Ruby 要來得好
最後最後講個題外話,跟本主題沒有關係。
說過很多次個人相當崇拜物件導向,也喜歡各種高度抽象、動態的特性。
而我 Ruby 特性看得越多,某種失落感就越大。
因為以我現在的能力,實在想不太到還有哪些是 Ruby 相當值得改進的部份。
也就是說,Ruby 讓我感覺不到有必要自己下去寫一個全新的語言,他已經夠好了。
剛開始碰 Ruby 時,勾起我很多的想法。可是越碰越多後,發現其實這些在
Ruby 裡面本來就已經有了,而且夠好用了。剩下的交由 lib 實作就可以了,
沒什麼語言可以置喙的地方…。
我想當然,一定還有很多可以改進的,只是以我現在的能力確實看不太出來。
之前貼過一篇文章,Ruby sucks (All those shaky laguages... )
http://www.lrde.epita.fr/~didier/comp/ruby.php
我一直很喜歡這篇,因為其實這篇對 Ruby 有很高的讚譽。
只是完全比不上 Lisp 而已,哈哈。而目前我還不太能夠理解 Lisp...
真的要說的話,目前我還是支持多重繼承,想不太到多重繼承有什麼大問題。
除了讓 compiler 變得很難寫、object system 變得錯綜複雜外,不覺得有什麼問題。
而每一個鋒銳的武器,當然都很有可能會傷到自己人,這是功力問題。
水能載舟亦能覆舟,但這不代表我們要把水給拋棄…。
The Truth about Multiple Inheritance
http://www.eiffel.com/general/monthly_column/2006/October.html
這篇也是在讚揚多重繼承。雖然我不是很懂他對 C++ 有什麼不滿。
在 Common Lisp 的 CLOS 中,多重繼承的威力更是非常非常地驚人。
當然我是不太清楚多有威力,畢竟 Lisp 還是離我有點遠,只是,
Ruby 如果加上多重繼承的話,也許是一個改進的方向吧。
2007.02.08
--
Nobody can take anything away from him.
Nor can anyone give anything to him.
What came from the sea,
has returned to the sea.
Chrono Cross
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.135.28.18
推
02/08 18:34, , 1F
02/08 18:34, 1F
推
02/08 20:15, , 2F
02/08 20:15, 2F
→
02/08 20:16, , 3F
02/08 20:16, 3F
→
02/08 20:40, , 4F
02/08 20:40, 4F
Ruby 近期熱門文章
PTT數位生活區 即時熱門文章