-1

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 độ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ó.

alt

h1. 1. Từ khóa @[email protected]

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.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí