Ruby: class << self
我們先不管class << self是什麼意思,我們先來看看動態語言有哪些有趣的用法。
Ruby可以動態的在class裡面新增,修改一些方法。譬如:
class Foo
def foo
return 'Hello Foo'
end
end
f = Foo.new
puts f.foo # Hello Foo
class Foo
def foo
return 'Edit Foo'
end
end
puts f.foo # Edit Foo
有玩過RPG Maker的人應該很熟練了,RPG Maker的模組都是這樣掛上去的,直接把模組複製到整個Script的最後面,將系統原先的方法override掉(這邊我講override其實有點問題,因為override比較適合用在編譯時期,子類別繼承並修改父類別的方法,但這裡單純只是修改類別原先的方法)
好的,那如果我產生兩個Foo物件,延續上例的類別
class Foo
def foo
return 'Hello Foo'
end
end
f1 = Foo.new
f2 = Foo.new
puts f1.foo # Hello Foo
puts f2.foo # Hello Foo
class Foo
def foo
return 'Edit Foo'
end
end
puts f1.foo # Edit Foo
puts f2.foo # Edit Foo
因為是直接對類別做修改,所以上例中,f1與f2的行為都會改變。
那如果我們今天想要做出像Javascript那種功能,只對單一物件新增或修改方法,有辦法做到嗎?譬如Javascript可以這麼做:
function Foo(){
}
Foo.prototype.foo = function(){
return "Hello foo";
}
var f1 = new Foo();
var f2 = new Foo();
console.log( f1.foo() ); // Hello foo
console.log( f2.foo() ); // Hello foo
// 只對物件f1修改方法
f1.foo = function(){
return 'Edit foo';
}
console.log( f1.foo() ); // Edit foo
console.log( f2.foo() ); // Hello foo
// 只對物件f1新增方法
f1.bar = function(){
return 'Hello bar';
}
console.log( f1.bar() ); // Hello bar
console.log( f2.bar() ); // ERROR, undefined
這麼動態呀~那Ruby呢?Ruby有沒有辦法做到,Ruby有個語法,看起來很像直接對單一物件,宣告出一個類別
class << some_object
直接來個可以執行的範例
class Foo
def foo
return 'foo'
end
end
f1 = Foo.new
f2 = Foo.new
puts f1.foo # foo
puts f2.foo # foo
# 只對f1 修改方法
class << f1
def foo
return 'Edit foo'
end
end
puts f1.foo # Edit foo
puts f2.foo # foo
# 只對f1 新增方法
class << f1
def bar
return 'bar'
end
end
puts f1.bar # bar
puts f2.bar # undefined method `bar'
好的,到這裡,回到我們的標題class << self
,這是什麼意思?可以用在什麼地方呢?
還記得Ruby每個東西都是物件,我們要對類別增加類別方法是怎麼做的嗎?
class Foo
def self.foo
return 'foo'
end
end
puts Foo.foo #foo
直接在類別裡,並非在物件會被呼叫到的地方使用self,則會指向Foo這個Class物件,所以我們直接對Foo這個Class物件加上方法,就會變成Foo的類別方法。
有沒有發現跟上面我們對單一物件新增或修改方法有點像?是的,既然Foo是個單一的Class物件,我們也可以單一的為它加上方法,成為類別方法。
class Foo
class << self
def foo
return 'foo'
end
end
end
class Bar
end
puts Foo.foo #foo
puts Bar.foo #undefined method `foo' for Bar:Class (NoMethodError)
foo加到Foo類別的成為它的類別方法,所以不影響Bar類別
主要是學習Ruby語言的一個動態特性,使用簡潔的程式碼達到這些目的。就跟JAVA 8也做出Lambda Function了一樣。
那麼實際上有什麼應用呢?譬如Design Pattern裡面的Visitor Pattern在Ruby裡面可以這麼應用:
class Good
attr_accessor :price
def initialize price
@price = price
end
def accept visitor
visitor.visit self
end
end
class TaxVisitor
def initialize rate
@rate = rate
end
def visit obj
if obj.respond_to? :price
class << obj
attr_accessor :tax
end
obj.tax = obj.price * @rate
end
end
end
good = Good.new 30
good.accept(TaxVisitor.new(0.25))
puts good.tax
我們對Good類別,accept稅金的訪問,然後就在訪問時,動態的為貨物加上了tax這個方法,可以對貨物存取稅金。這個範例寫得不太好,其實price與tax應該要對外關閉,寫成唯讀才對
另外,物件也可以動態的掛載module
module Foo
def foo
puts 'foo'
end
end
obj1 = Object.new
obj2 = Object.new
obj1.extend Foo
obj1.foo # => foo
obj2.foo # => 錯誤,NoMethodError
沒有留言:
張貼留言