Ruby Metaprogramming - The Basics
Qua 3 bài trong loạt bài về metaprogramming trong ruby, tôi đã nói về eval, send, define_method, hẳn bạn đã thấy sự kì diệu mà metaprogramming mang lại cho chúng ta. Nhưng đi sâu thêm một chút nữa, có lẽ chúng ta nên tìm hiểu và nên biết tại sao metaprogramming có thể hoạt động được và nó đã hoạt ...
Qua 3 bài trong loạt bài về metaprogramming trong ruby, tôi đã nói về eval, send, define_method, hẳn bạn đã thấy sự kì diệu mà metaprogramming mang lại cho chúng ta. Nhưng đi sâu thêm một chút nữa, có lẽ chúng ta nên tìm hiểu và nên biết tại sao metaprogramming có thể hoạt động được và nó đã hoạt động như thế nào. Trong bài này, bài cuối cùng trong loạt bài về metaprogramming trong ruby, tôi sẽ cùng các bạn đi sâu vào mô hình đối tượng của Ruby để tìm hiểu cách vận hành của chúng.
Dưới đây là mô hình đối tượng của Ruby, chúng ta hãy cùng nhau phân tích nó.
h1. 1. Từ khóa @self@
Như bạn đã biết, mọi thứ trong Ruby đều là đối tượng, bao gồm cả class. Như ở đây, Developer là một class, nó sẽ được hiểu là một instance của class Class. Chẳng hạn như thế này:
Developer = Class.new do def self.meth1; "Hello"; end def meth2; "Bye"; end end a = Developer.new #=> #<#<Class:0x100381890>:0x100376b98> Developer.meth1 #=> "Hello" a.meth2 #=> "Bye"
Ta có một cấu trúc các class được kế thừa lẫn nhau:
p Developer.class # Class p Class.superclass # Module p Module.superclass # Object p Object.superclass # BasicObject
Một điều quan trọng cần hiểu ở đây là ý nghĩa của từ khóa self. Tất cả những mã thực thi trong Ruby đều chứa một biểu hiện cụ thể, nó chính là self. self luôn đề cập đến một đối tượng cụ thể nhưng nó có thể thay đổi theo đoạn mã được thực thi. Ví dụ, bên trong một khai báo class, self thể hiện cho chính instance của Class:
class Developer p self end # Developer
Trong một instance method, self thể hiện một instance của class đó:
class Developer def frontend self end end p Developer.new.frontend # #<Developer:0x2c8a148>
Trong một class method, self thể hiện cho class của chính nó
class Developer def self.backend self end end p Developer.backend # Developer
Có khi nào bạn tự hỏi, tại sao lại có khai báo class method như trên, và class method là gì? Trước khi trả lời câu hỏi đó, tôi muốn nhắc đến một khái niệm là metaclass, hay còn được gọi với cái tên singleton class và eigenclass. Phương thức backend ở trên chính là một instance được khai báo trong metaclass của class Developer.
h1. 2. Metaclass
Tất cả mọi đối tượng trong Ruby đều chứa một metaclass, nó có thể là vô hình đối với bạn, nhưng nó vẫn tồn tại và bạn có thể sử dụng nó. Class Developer ở trên cơ bản là một đối tượng, và tất nhiên nó cũng có một metaclass. Một metaclass về cơ bản là một class được Ruby tạo ra và chèn vào các hệ thống phân cấp thừa kế để giữ các phương thức class, do đó không can thiệp với các instance được tạo ra từ các class. Hãy cùng làm một ví dụ nho nhỏ với metaclass:
example = "I'm a string object" def example.something self.upcase end p example.something # I'M A STRING OBJECT
Trong ví dụ này, ta đã tạo ra một singleton method là something cho một đối tượng example. Sự khác biệt giữa class methods và singleton methods: trong khi class methods có thể sử dụng với tất cả instance của một class thì singleton methods chỉ có thể sử dụng cho duy nhất một instance. Các class methods được sử dụng nhiều nhưng singleton methods được sử dụng ít hơn, tuy nhiên chúng đều được thêm vào metaclass của một đối tượng.
Ví dụ bên trên có thể được viết bằng cách khác:
example = "I'm a string object" class << example def example.something self.upcase end end
Bây giờ hãy quay lại với ví dụ khai báo class Developer ở bên trên và cùng thử một số cú pháp để khai báo một class method:
class Developer def self.backend "I am backend developer" end end
Một cách tường minh hơn:
def Developer.backend "I am backend developer" end
Hoặc như chúng ta vẫn thường viết:
class << self def backend "I am backend developer" end end
Đầu tiên chúng ta khai báo class << self để ám chỉ đến metaclass của class Developer, và sau đó khao báo phương thức backend ở trong metaclass của class Developer. Bằng cách khai báo như thế này, chúng ta đang thêm phương thức backend vào trong metaclass của clas Developer, thay vì thêm vào chính class đó. Nói cách khác, phương thức backend là một instance của metaclass, của class Developer.
Và như vậy, chỉ cần hiểu rõ về object, class, method cũng như cấu trúc của nó là chúng ta đã có thể sử dụng các phương thức eval, send,... để lập trình metaprogramming một cách thuần thục hơn.