[Day20] 接下來要隆重登場的是,鋼!鐵!將!軍!
嗨大家好!這篇要介紹的是開放類別(Open Class)!
過去幾天,我已經學會了類別、繼承、模組、實體方法與類別方法的差異,以及方法存取的限制。而今天再來看一些有趣的東西吧!
身為一隻金魚,我常常在 coding 時忘記自己有沒有寫過這個類別,一不小心就做出了兩個相同名稱的類別,但裡面各自定義的方法不同:
class Robot
def eye
puts "發射超強激光!"
end
end
class Robot
def arm
puts "發射拳頭砲彈!"
end
end
這時候如果又做了一個實體 franky
出來:
franky = Robot.new
那這個實體 franky
到底是從哪個類別產生的呢? 不是很確定,
那讓它呼叫實體方法看看:
franky.eye
franky.arm
# 印出
發射超強激光!
發射拳頭砲彈!
咦!franky
居然兩招都可以用!
難道是...?
事實上,Ruby 會把我們寫的東西看成這樣:
class Robot
def eye
puts "發射超強激光!"
end
def arm
puts "發射拳頭砲彈!"
end
end
居然合體啦!!!只要是同名的類別,各自定義的方法也會融合在一起!
(同名方法自然另當別論)
之前介紹時好像沒提到 Ruby 這個有趣的特性,就拿剛剛的 Robot
做例子吧!
class Robot
end
franky = Robot.new
現在我自己定義了一個新的類別 Robot
,可是裡面真的是超~空~一個方法都沒有。
然後 franky
能呼叫什麼方法嗎?應該不行吧?
還是來確認一下好了:
franky.methods
# 印出
[:dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :clone, :display, :hash, :class, :singleton_class, :method, :public_send, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :nil?, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :__send__, :!, :==, :__id__, :!=, :equal?, :instance_eval, :instance_exec]
哇啊啊一堆方法!這些是哪裡來的?是誰偷駭進了我的電腦嗎?
不對不對這一定是在做夢,我要來測試一下!
Q1: franky
是什麼類別?
franky.class
=> Robot
通過第一關...沒關係再來!
Q2: franky
是整數嗎?
franky.is_a?(Integer)
=> false
好我知道很明顯不是...
啊!等等!就連一開始找方法的:
franky.methods
這個 methods
也不是我自己定義的啊!虧我還用得這麼順手!
不管是原生類別還是自定義類別,其實 Ruby 都已經先準備好很多方法著大家去使用了!這部分的原因有興趣的朋友可以先上網查 Ruby 的 Kernel
模組,有時間我會再來仔細說明!
由於這個特性,我們就能為原生類別再添加一些自定義的方法,這個技巧也是 開放類別(Open Class) 的精髓。
譬如 Ruby 原生的 String
類別,裡面已經定義好很多給字串使用的方法:
a = String.new
a.methods
# 印出
[:unicode_normalize, :unicode_normalize!, :ascii_only?, :to_r, :unpack, :unpack1, :encode!, :%, :include?, :*, :+, :count, :partition, :+@, :-@, :<=>, :<<, :to_c, :==, :===, :sum, :=~, :next, :[], :casecmp, :casecmp?, :insert, :[]=, :match, :match?, :bytesize, :empty?, :eql?, :succ!, :next!, :upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :scrub!, :scrub, :undump, :byteslice, :freeze, :inspect, :capitalize, :upcase, :dump, :downcase!, :swapcase, :downcase, :hex, :capitalize!, :upcase!, :lines, :length, :size, :codepoints, :succ, :split, :swapcase!, :bytes, :oct, :prepend, :grapheme_clusters, :concat, :start_with?, :reverse, :reverse!, :to_str, :to_sym, :crypt, :ord, :strip, :end_with?, :to_s, :to_i, :to_f, :center, :intern, :gsub, :ljust, :chars, :delete_suffix, :sub, :rstrip, :scan, :chomp, :rjust, :lstrip, :chop!, :delete_prefix, :chop, :sub!, :gsub!, :delete_prefix!, :chomp!, :strip!, :lstrip!, :rstrip!, :squeeze, :delete_suffix!, :tr, :tr_s, :delete, :each_line, :tr!, :tr_s!, :delete!, :squeeze!, :slice, :each_byte, :each_char, :each_codepoint, :each_grapheme_cluster, :b, :slice!, :rpartition, :encoding, :force_encoding, :valid_encoding?, :hash, :unicode_normalized?, :encode, :clamp, :between?, :<=, :>=, :>, :<, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :clone, :display, :class, :singleton_class, :method, :public_send, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :!~, :nil?, :respond_to?, :object_id, :send, :__send__, :!, :__id__, :!=, :equal?, :instance_eval, :instance_exec]
現在我們來把幫 String
類別加上一個 say_hello
方法:
class String
def say_hello
"晚安, 接下來由#{self}來帶您學習 Ruby~"
end
end
"娜美".say_hello
=> "晚安, 接下來由娜美來帶您學習 Ruby~"
"布魯克".say_hello
=> "晚安, 接下來由布魯克來帶您學習 Ruby~"
這樣的話,剛剛 a.methods
列出來的方法裡,就會多一個我們剛剛定義的 sayhello
方法囉!
另外,在這裡的 self
指的就是呼叫方法的 receiver
本身。
除了像剛剛那樣幫類別添加方法,我們甚至也能覆蓋掉 Ruby 原先寫好的方法,這樣的行為通常被稱作 "Monkey Patch",這就是 Ruby 語言的一大特點:可動態修改
1 + 1 = 2
是數學上顛撲不破的真理,但我們來看個例子:
1 + 1
=> 2
上次有提過在 Ruby 裡,其實是這樣運作:
1.+(1)
=> 2
當我們知道其實是 1
這個物件呼叫了 +
方法,再帶入 1
這個參數後,就可以自己來改寫 +
方法:
1.class
=> Integer
class Integer
def +(n)
return n
end
end
我讓 +
這個方法變成只會回傳加上去的數字,所以這時候再使用 +
的話,就會變成:
puts 1 + 1
puts 2 + 1
puts 3 + 1
#印出
1
1
1
得到的通通都是 1
了!數學已死!!!
今天終於來到 20 天了!開放類別就先介紹到這邊啦!後面會再提到類似的概念可能就要到淺談 Ruby 物件導向的篇章了!希望能順利完賽!
最後送大家一句金句:投資一定有風險基金投資有賺有賠使用 Monkey Patch 前應詳閱 Ruby 官方文件說明書
話已帶到,使用時請小心、保重!