Eigenclasses trong ruby - Ẩn số thú vị
Khi làm việc với ruby and rails chúng ta phải làm việc rất nhiều với class, chính vì vậy mà việc hiểu và sử dụng class trong ruby là rất quan trọng. Bài viết này mình sẽ tìm hiểu về một khái niệm liên quan đến class trong ruby đó là Eigenclasses.
1. Singleton method
Trước khi đi sâu hơn về class, hãy cùng tìm hiểu về singleton method trong ruby. Có nhiều cách định nghĩa về singleton method trong ruby nhưng mình hiểu đơn giản là:
Ruby cho phép ta thêm một method vào một đối tượng
VD: Ta có một đối tượng str = "hello"
Chúng ta có thể thêm một method vào đối tượng str này bằng cách khai báo:
def str.method1
puts "this is method 1"
end
Như vậy khi ta gọi str.method1 => kết quả in ra là "this is method 1". Tuy nhiên, chỉ có đối tượng str mới có thể gọi method 1, với những object khai báo và khởi tạo bằng string khác sẽ không thể gọi method1.
str2 = "hello2"
str2.method1 # No method 'method1'
Chúng ta cần nhớ lại là mọi thứ trong ruby là đối tượng kể cả class. Như vậy khi ta gọi một class method cũng giống như việc ta gọi một object của đối tượng như ví dụ phía trên.
class A; end
# khai báo một method cho class A cũng giống như khai báo cho đối tượng
def A.method1
puts "this is method of A"
end
A.method1 # 'this is method of A'
Và sự thực là class method chính là singleton method của class. Ta cũng có thể thay tên của class như ví dụ trên bằng từ khóa self
class A
def self.method1
puts "this is method of A"
end
end
Như vậy chúng ta luôn định nghĩa singleton method giống như mẫu
def object.method
# implement method here
end
2. Eigenclasses
Sau khi đã tìm hiểu cơ bản về method trong ruby, phần này ta sẽ đi sâu tìm hiểu về khái niệm Eigenclasses.
Ta có một class
class MyClass
def my_method; end
end
obj = MyClass.new
obj.my_method
Khi ta gọi hàm my_method từ đối tượng obj ruby sẽ tìm trực tiếp từ class MyClass, nếu không tìm thấy nó sẽ tìm kiếm từ các class cha mà MyClass kế thừa. Trong trường hợp này thì nó sẽ tìm thấy luôn trong class MyClass.
Nhưng giả sử chúng ta khai báo singleton method cho obj
def obj.my_singleton_method; end
Nhìn vào sơ đồ
Ta tự hỏi cái method my_singleton_method nó nằm ở đâu vậy (???). Nó không thể nằm trong obj được vì obj không phải là 1 class, cũng không thể nằm trong class MyClass được vì như vậy mọi instance của MyClass sẽ gọi được hàm my_singleton_method trong khi thực tế thì chỉ có obj mới gọi được method này thôi, nó cũng không thể nằm trong các class mà MyClass kế thừa cũng vì lý do này. Vậy thì ở đâu ???
Đây chính là lúc thằng eigenclass bị phơi bày ra.
Khi chúng ra hỏi về class của một object, ta thường nghĩ đến class đã định nghĩa nhưng sự thật object có một class ẩn gọi là eigenclass.
Ta có thể thêm code cho eigenclass với cách khai báo sau:
class << object
# your code here
end
Chúng ta sẽ đi vào các ví dụ sau để hiểu sâu hơn về thằng này.
class C
def a_method
'C#a_method()'
end
end
class D < C; end
obj = D.new
obj.a_method # kết quả : 'C#a_method()'
Khi ta gọi hàm a_method ruby sẽ tìm kiếm từ class D, sau khi không tìm kiếm thấy method a_method nó sẽ đi lên class C để tìm kiếm vì D kế thừa C
Sơ đồ sẽ như sau:
Tiếp theo để dễ dàng cho việc tìm hiểu eigenclass chúng ta định nghĩa một method sau ở class Object
class Object
def eigenclass
class << self; self; end
end
end
"string".eigenclass # kết quả là #<Class:#<String:0x00005593e58c3920>>
Nhìn vào kết quả trên ta thấy ruby trả về eigenclass của một đối tượng và để phân biệt nó thêm "#" vào phía trước. Như vậy tương tự eigenclass của C là #C, của D là #D, của obj là #obj. Khi ta định nghĩa một method cho obj như sau:
class << obj
def a_singleton_method
'obj#a_singleton_method()'
end
end
obj.a_singleton_method # kết quả: 'obj#a_singleton_method()'
obj.eigenclass.superclass # => D
Sơ đồ lúc này sẽ như sau:
Nhìn vào sơ đồ ta có thể trả lời câu hỏi phía trên đó là hàm a_singleton_method thực tế là nằm ở eigenclass của obj đó là #obj . Và class này kế thừa D (wow)
Tiếp theo chúng ta sẽ tìm hiểu về sự liên hệ giữa class, eigenclass và giữa các eigenclass với nhau
Ta khai báo tiếp cho class C như sau:
class << C
def a_class_method
'C.a_class_method()'
end
end
# gọi các hàm sau
C.eigenclass # => #<class: C>
D.eigenclass # => #<class: D>
C.eigenclass.superclass # => #<class: Object>
D.eigenclass.superclass # => #<class: C>
D.a_class_method # kết quả 'C.a_class_method()'
Từ ví dụ trên ta có sơ đồ sau
Có thể thấy super class của #C là #Object (eigenclass của object), super class của #D là #C. Và để cho dễ chúng ta chỉ cần nhớ:
Super class một eigenclass chính là eigenclass của super class đó :v
Cũng từ sơ đồ trên ta có thể thấy D có thể gọi method a_class_method vì eigenclass của D kế thừa eigenclass của C (hoplyha)
3. Kết luận
Như vậy bài viết của mình đã trình bày về method, class và thằng ẩn danh eigenclass. Hi vọng bài viết sẽ giúp ích cho các bạn, nếu có gì cần thảo luận hay góp ý hãy để lại bình luận phía dưới nhé. (seeyou)
Reference
Cuốn sách 'Metaprogramming Ruby'
All rights reserved