06/04/2021, 14:47

Hiểu về Method Missing trong Ruby - Ruby căn bản

Trong bài này chúng ta sẽ tìm hiểu Method Missing trong Ruby, đây là một khái niệm muốn nói đến một phương thức không tồn tại trong Ruby, nên hiểu nó sẽ giúp ích cho bạn rất nhiều trong quá trình làm việc với Ruby. 1. Method missing là gì ? Chắc hẳn với các lập trình viên chúng ta ai cũng có ...

Trong bài này chúng ta sẽ tìm hiểu Method Missing trong Ruby, đây là một khái niệm muốn nói đến một phương thức không tồn tại trong Ruby, nên hiểu nó sẽ giúp ích cho bạn rất nhiều trong quá trình làm việc với Ruby.

1. Method missing là gì ?

Chắc hẳn với các lập trình viên chúng ta ai cũng có thể đôi lần dù cố tình hay vô tình gọi đến một method không tồn tại. Điều này thì chắc chắn sẽ gây ra thông báo lỗi mặc định của Ruby rồi.

Thế nhưng Ruby đã xử lý như thế nào với những method không tồn tại này thì trong bài này mình sẽ giới thiệu cho các bạn một phương thức mà Ruby đã gọi tới là chính là method missing.

Đúng như tên gọi của nó method_missing, thì chúng ta cũng có thể hiểu được nôm na chức năng của phương thức này là gì. Nó sẽ được gọi tới khi mà một method được gọi không tồn tại.

2. Method lookup

Để có một cái nhìn rõ hơn về method missing thì trước tiên chúng ta nên tìm hiểu xem khi nào thì method này được gọi tới.

class Animal
  def hello
    puts "Hello animal"
  end
end

class Dog < Animal

end

a = Dog.new

puts a.hello # Hello animal

Theo như ví dụ ở trên thì chúng ta có một đối tượng được tạo ra từ class Dog được kế thừa từ class Animal. Chúng ta gọi đến một phương thức có tên là hello, thế nhưng rõ ràng trong class Dog** không hề tồn tại do đó nó đã tiếp tục gọi đến superclass của nó đó là Animal.

Và cứ như thế nếu như Animal không tồn tại phương thức hello thì nó sẽ lần lượt gọi tới các superclass khác để tìm kiếm phương thức này. Nếu như không tìm thấy thì method cuối cùng được gọi tới chính là method_missing. Đây là một private method được xây dựng sẵn của class BasicObject.

Mà theo như Ancestors chain của một class thì

Class.ancestors

=> [Class, Comparable, Object, Kernel, BasicObject]

Nếu như bạn không biết Ancestors chain là gì thì mình sẽ giải thích đơn giản như sau

  • Tất cả các class mà nó kế thừa.
  • Tất cả các module được mix trong nó.

Do class được kế thừa từ tổ tiên của nó là BasicObject nên nó sẽ sử dụng được method_missing.

Nếu như bạn hỏi tại sao mà mình biết nó thuộc class BasicObject thì mở terminal lên và gõ ngay lệnh

BasicObject.private_methods.sort

để xác thực nhé :D

Trong cây phân lớp class thì BasicObject là tổ tiên cuối cùng của mọi class khác.

3. No method error

Chúng ta có một ví dụ đơn giản

class Dog
    def talk
       puts "Dog go go"
   end
end

dog = Dog.new

puts dog.talk

Kết quả

Dog go go

Kết quả vẫn hoạt động bình thường thế nhưng hãy thử gọi lại một phương thức không tồn tại.

puts dog.eat

# Kết quả

Traceback (most recent call last):

method_missing.rb:8:in `<main>': undefined method `eat' for #<Dog:0x000055632c3cc550> (NoMethodError)

Vậy là con dog của chúng ta không có chức năng ăn rồi, chúng ta sẽ nhận được ngay được thông báo lỗi của chương trình NoMethodError, và cũng thấy Ruby log luôn lỗi tại file nào và dòng nào luôn theo như log được hiển thị.

Thế nhưng bây giờ thay vì muốn nhận được một Exception error thì chúng ta muốn nhận được một message rõ ràng hơn thay vì nhận được một message mặc định của Ruby. Thì đây là lúc chúng ta sử dụng tới method_missing rồi đây.

4. Cú pháp và cách sử dụng

Để khai báo một method missing trong Ruby thì ta sử dụng cú pháp sau.

def method_missing method_name, *args, &block
  # code
end

Cú pháp cũng giống như các phương thức khác và trong method này sẽ nhận vào các tham số như

  • method_name: Tên của phương thức được gọi tới mà không tồn tại/ không được định nghĩa
  • *arg: Đây là một mảng các tham số chứa các giá trị của các tham số được truyền vào phương thức không tồn tại
  • &block: Tham số này không bắt buộc, nó thể hiện phương thức đang được gọi có đi kèm một block ở tận cùng.

Thử viết lại một message khác mà chúng ta muốn thay vì là sử dụng Exception Error của Ruby mặc định. Để làm được việc này chúng ta chỉ cần overwrite lại phương thức method_missing của class BasicObject.

class Dog
    def talk
       puts "Dog go go"
   end

   def method_missing method_name, *arg
      if method_name.to_s == 'eat'
        puts "Dog don't need eat"
      else
        super
      end
    end
end

dog = Dog.new

puts dog.eat

Kết quả khi chạy chương trình

Dog don't need eat

Kết quả đã ra đúng như mong muốn. Giờ tiếp tục thử với một method không tồn tại khác xem sao

puts dog.run

# kết quả

method_missing.rb:10:in `method_missing': undefined method `run' for #<Dog:0x0000563740d04210> (NoMethodError)

Kết quả lại trả về NoMethodError như ban đầu. Đơn giản vì trên đoạn code trên chúng ta chỉ kiểm tra đối với nếu method được gọi tới có tên là eat còn nếu không sẽ nhảy vào else và gọi tới super. Từ khóa này sẽ gọi lại phương thức method_missing của BasicObject.

2. Kết luận

Qua bài bài này chúng ta đã cùng đi tìm hiểu xong cách sử dụng một method missing trong Ruby, từ đó cũng hiểu luôn khi nào thì phương thức này được gọi tới.

Khi mà các bạn làm việc với Ruby on Rails (một framework của Ruby) thì đã có một vài các phương thức mà họ sử dụng method_missing để định nghĩa các hàm có sẵn có thể gọi, nếu không nó cũng sẽ gọi tới keyword super để gọi phương thức method_missing trong BasicObject giống như cách mà chúng ta đã làm ở trên.

Tạ Quốc Bảo

23 chủ đề

7270 bài viết

0