12/08/2018, 14:37

Ruby Metaprogramming - Creating Methods

Creating Methods Trong bài viết này tôi sẽ nói về một khía cạnh của lập trình meta programming trong Ruby. Khả năng về tạo ra methods dynamically, trong suốt thời gian chạy. Có khá nhiều lý do để giải thích tại sao chúng ta nên làm điều này, một trong số đó là cho phép chúng ta viết ra generator ...

Creating Methods

Trong bài viết này tôi sẽ nói về một khía cạnh của lập trình meta programming trong Ruby. Khả năng về tạo ra methods dynamically, trong suốt thời gian chạy. Có khá nhiều lý do để giải thích tại sao chúng ta nên làm điều này, một trong số đó là cho phép chúng ta viết ra generator methods để giúp chúng ta tránh được việc viết code lặp đi lặp lại.

The Long Way

Nếu không có gì đặc biệt xảy ra, các method dưới đây không thực sự làm được gì nhiều ngoài việc get và set 1 biến instance.

class Alpaca
  def initialize
    @name
  end

  def name
    @name
  end

  def name=(value)
    @name = value
  end
end

buddy = Alpaca.new("Buddy")
buddy.name # Buddy

Điều này thực sự gây phiền nhiều, tốn thời gian để viết ... Và trong ruby có một cách dễ dàng để có được chức năng tương tự với chỉ tốn 1 dòng code. attr_accessor Ruby cho chúng ta 3 methods để giúp tạo ra getter, setter cho biến instance. attr_reader tạo getter, attr_writer tạo setter, attr_accessor tạo cả 2. Để sử dụng, chúng ta chỉ cần định nghĩa phía trên đầu của class và ruby sẽ tạo ra getter, setter cho chúng ta.

class Alpaca
  attr_accessor :name
  def initialize(name)
    self.name = name
  end
end

buddy = Alpaca.new("Buddy")
puts buddy.name # "Buddy"

Let's do this ourselves

Bởi vì Ruby là dynamic laguage, nó cho phép chúng ta thêm mới hoặc xóa đi các method trong lúc chạy chương trình. Chúng ta hãy cùng tái hiện lại những gì Ruby đã làm gì khi chúng ta sử dụng attr_accessor. Tôi sẽ gọi nó là getset, getter, setter. Chúng ta có thể sử dụng define_method để tự động tạo ra 1 method mới. Để hoàn hành method, tôi sẽ sử dụng tiếp các method instance_variable_get và instance_variable_set để thiết lập các biến instance trong hàm được định nghĩa.

# Let's create a base class to extend from. 
# This class contains the code generator methods that we'll be using.
class Base
  def self.getset(*args)
    args.each do |field|
      getter(field)
      setter(field)
    end
  end

  def self.getter(*args)
    args.each do |field|
      define_method(field) do
        instance_variable_get("@#{field}")
      end
    end
  end

  def self.setter(*args)
    args.each do |field|
      define_method("#{field}=") do |value|
        instance_variable_set("@#{field}", value)
      end
    end
  end
end

# Now let's create a class and utilize our getset generator
class Alpaca < Base
  # We'll create accessors for :name and :age
  getset :name, :age

  def initialize(name, age)
    self.name = name
    self.age  = age
  end
end

buddy = Alpaca.new("Buddy", 24)
# Let's call our methods and make sure they return what we expect
puts buddy.name # Buddy
puts buddy.age # 24

# Let's see if our object responds to the new methods we created
puts buddy.respond_to?(:name) # true
puts buddy.respond_to?(:name=) # true

Concluding thoughts

Nếu bạn là Rails developer, có lẽ bạn đã từng sử dụng một method được tự động sinh ra trước khi bạn biết hoặc là chưa biết về nó.Trong model của rails có khá nhiều methods được tạo ra tự động cho mỗi thuộc tính của model đó. Đây là lý do tại sao bạn có thể gọi @user.name mà không cần phải tạo ra phương thức getter cho thuộc tính trên.

Bài viết được dịch tại nguồn https://www.leighhalliday.com/ruby-metaprogramming-creating-methods

0