này hoạt động (thử nghiệm trong IRB):
Chú ý: Đây chỉ thay đổi str
- không phải tất cả các trường hợp của String. Đọc dưới đây để biết chi tiết là tại sao các công trình này
another_str = "please don't change me!"
str = "ha, try to change my to_s! hahaha!"
proc = Proc.new { "take that, Mr. str!" }
singleton_class = class << str; self; end
singleton_class.send(:define_method, :to_s) do
proc.call
end
puts str.to_s #=> "take that, Mr. str!"
puts another_str.to_s #=> "please don't change me!"
# What! We called String#define_method, right?
puts String #=> String
puts singleton_class #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
này hoạt động bởi vì bạn đang mở str của singleton class và xác định một phương pháp đó. Bởi vì điều này, cũng như các cuộc gọi đến Module#define_method, có những gì một số gọi là "phạm vi bằng phẳng", bạn có thể truy cập các biến nằm ngoài phạm vi nếu bạn sử dụng def to_s; 'whatever'; end
.
Bạn có thể muốn kiểm tra một số trong những "phép thuật lập trình meta" khác tại đây:
media.pragprog.com/titles/ppmetr/spells.pdf
Tại sao nó chỉ thay đổi str
?
Vì Ruby có một vài thủ thuật thú vị lên đó là tay áo. Trong mô hình đối tượng Ruby, một phương thức gọi kết quả trong bộ thu không chỉ tìm kiếm lớp (và nó là tổ tiên), mà còn là lớp đơn (hoặc như Matz gọi nó là eigenclass). Lớp singleton này cho phép bạn định nghĩa một phương thức cho một đối tượng đơn lẻ. Những phương pháp này được gọi là "phương pháp singleton". Trong ví dụ trên, chúng ta đang làm điều đó - định nghĩa một tên phương thức singleton to_s
. Đó là functionaly giống hệt như thế này:
def str.to_s
...
end
Sự khác biệt duy nhất là chúng tôi có được sử dụng một đóng cửa khi gọi Module#define_method
, trong khi def
là một từ khóa, mà kết quả trong một sự thay đổi phạm vi.
Tại sao nó không đơn giản hơn?
Vâng, những tin tức tốt lành là bạn đang lập trình trong Ruby, vì vậy cảm thấy tự do để phát điên:
class Object
def define_method(name, &block)
singleton = class << self; self; end
singleton.send(:define_method, name) { |*args| block.call(*args) }
end
end
str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }
puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!
Và, nếu bạn tò mò ...
Bạn biết phương pháp lớp học? Hoặc, trong một số ngôn ngữ, chúng ta sẽ gọi chúng là phương pháp tĩnh? Vâng, những người không thực sự tồn tại trong Ruby. Trong Ruby, các phương thức lớp thực sự chỉ là các phương thức được định nghĩa trong lớp đơn lớp của đối tượng Class.
Nếu điều đó nghe có vẻ điên rồ, hãy xem các liên kết tôi đã cung cấp ở trên. Rất nhiều sức mạnh của Ruby chỉ có thể được khai thác nếu bạn biết làm thế nào để metaprogram - trong trường hợp đó bạn sẽ thực sự muốn biết về các lớp/phương thức singleton, và nói chung, mô hình đối tượng Ruby.
HTH
Charles
Nhưng tôi cần phải sửa đổi nó chỉ trong một đối tượng, không phải trong tất cả (trong lớp). – Pablo
Đó là những gì nó làm. Mỗi đối tượng có những gì được gọi là một lớp singleton - đó là lớp riêng của cá nhân để lưu trữ các phương thức. Kiểm tra nó ra - bạn sẽ thấy rằng các trường hợp khác của String không bị ảnh hưởng. Tôi sẽ cập nhật câu trả lời của tôi với một bài kiểm tra, để cho thấy rằng nó hoạt động. – Charles
Ồ, sai lầm của tôi, tôi sẽ thử. – Pablo