Hook method trong ruby
Bài đăng này đã không được cập nhật trong 7 năm
Hook methods là gì
- Hook method là những phương thức đặc biệt của Ruy cung cấp một cách mở rộng hoạt động của chương trình tại thời điểm runtime
- Hook method giống như việc đăng kí một sự kiện và sự kiện đó sẽ được callback bởi một chỗ khác
- Khi trình biên dịch đọc đến dòng code mà thấy có sự kế thừa thì nó sẽ callback đến một hàm của class cha, hàm đó chính là một hook method
- Chúng ta sẽ tìm hiểu về các hook methods sau: inherited included extended prepended method_missing
Inherited
Inherited là một mối quan hệ giữa các class. Chúng ta biết rằng mèo là một loại động vật có vú và động vật có vú lại là động vật. Lời ích của việc thừa kế là các class mức thấp hơn trong cây thừa kế sẽ có được những đặc điểm của các class cao hơn trong dãy thừa kế không chỉ vậy nó còn có thể thềm những đặc điểm riêng biệt của nó. Nếu tất cả động vật có vú có thể thở thì mèo cũng có thể thở. Trong ruby một class chỉ có thể thừa kế từ 1 class khác. Nhiều ngôn ngữ khác support việc đa thừa kế, đặc điểm này cho phép class có thể thừa kế từ nhiều class khác nhau nhưng trong ruby thì không support điều này.
class Mammal
def breathe
puts "inhale and exhale"
end
end
class Cat < Mammal
def speak
puts "Meow"
end
end
rani = Cat.new
rani.breathe
rani.speak
Mặc dù chúng ta không có hàm cụ thể rằng mèo có thể thở, mỗi con mèo sẽ thừa kế các hành vi kia từ class Mammal vì vậy Cat sẽ được định nghĩa như một subclass của Mammal. Vì vậy mà từ một quan điểm của các lập trình viên, mèo có khả năng thở và sau đó chúng ta thêm phương thức nói nữa thì mèo vừa có 2 đặc điểm thở và nói.
Có những trường hợp mà ở đó những thuộc tính của supper-class sẽ không được thừa kế bời một subclass cụ thể. Mặc dù chim là hoàn toàn biết bay nhưng chim cánh cụt là không thể bay được. Các bạn tham khảo ví dụ sau.
class Bird
def preen
puts "I am cleaning my feathers."
end
def fly
puts "I am flying."
end
end
class Penguin < Bird
def fly
puts "Sorry. I'd rather swim."
end
end
p = Penguin.new
p.preen
p.fly
Inheritance cho phép bạn tạo một class với những phương thức được sử dụng lại hoặc được chuyển hòa từ phương thức của các class khác vì vậy mà class được tạo ra sẽ gọn gàng hơn. Một class chỉ có thể thừa kế từ một class tại một thời điểm nhất định.
Included
- include mang tất cả những method của module về làm instance method của class
- Tương tự inherite từ một class ta include một module này khi trình biên dịch đọc đến kí tự include thì nó sẽ callback tới method self.incluđe của module cha.
module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Bar.new.foo # heyyyyoooo!
Trong đó class Bar đã include module Foo và sử dụng các method của Foo nhưng những instance method
Extended
- Khi một class include một module thì các method trong module sẽ trở thành các class method của class đó
class Bar
extend Foo
end
Bar.foo # heyyyyoooo!
Trong đó class Bar extend module Foo và sử dụng các method của module Foo như những class method của mình
Prepended
Cách tốt nhất để hiểu được Module#prepend làm việc như thế nào chúng ta sẽ tìm ra các mà nó sử lý trong ruby Chúng ta bắt đầu với ví dụ sau
class Human
def hi
"Hi from Human!"
end
end
class Man < Human
end
Man.new.hi # => Hi from Human!
Đó là một cách dễ nhất vào rõ ràng nhất về thừa kế mà ai cũng biết. Method trong class cha đã được gọi đến
Tiếp theo hãy xem trường hợp thú vị sau khi mà method có cùng tên trong cả class và module cha.
class Human
def hi
"Hi from Human!"
end
end
module Greetable
def hi
"Hi from Module!"
end
end
class Man < Human
include Greetable
end
Man.new.hi # => "Hi from Module!"
Như chúng ta đã thấy, trong trường hợp này phương thức trong module đã được gọi đến. Vậy trong cây thừa kế, module có độ ưu tiên cao hơn class cha. Chúng ta có thể sử dụng method ancestor để kiểm tra:
Man.ancestors # => [Man, Greetable, Human, ...]
Điều đó chỉ ra rằng để tìm một method, Ruby sẽ tìm trong class Man trước sau đó đến module Greetable rồi đến Human class.
Thôi chúng ta quay lại với Module#presend
Chúng ta sẽ kiểm tra xem điều gì sẽ xảy ra nếu chúng ta thay đổi inlcude Greetable to presend Greetable Phương thức ancestors chỉ ra sự khác sau khi thay đổi include thành prepend
Man.ancestors # => [Greetable, Man, Human, ...]
Như chúng ta nhìn thấy Greetable là phần tử đầu tiền trong chuỗi các tổ tiền của Man. Nó có nghĩa rằng thậm chí chúng ta thêm method hi trong class Man thì nó cũng sẽ không gọi nó.
module Greetable
def hi
"Hi from Module!"
end
end
class Man
prepend Greetable
def hi
"Hi from Man"
end
end
Man.new.hi # => "Hi from Module!"
Man.ancestors.inspect # => [Greetable, Man, Object]
Việc này làm thay đổi cách tìm kiếm hàm trong ruby vậy nên bạn nên cẩn thận khi sử dụng nó
Method_missing
Kernel#method_missing() được gọi khi Ruby gửi một thông điệp và người nhận không tìm ra phương thức để trả về. Kernel là một module được đưa vào Object và hầu như tất cả các Ruby objects đều được thừa kế từ Object vì vậy tất cả các Ruby methods sẽ trả về method_missing. Method_missing có thể được ghi đè để thực thi các hành vi theo ý muốn của lập trình viên.
class Dude
def life_outlook
'cowabunga'
end
def method_missing(name, *args)
'radical haircut'
end
end
d = Dude.new
p d.undefined_method # => 'radical haircut'
p d.life_outlook # => 'cowabunga'
Khi một thực thể của class Dude sử một thông điệp mà nó có thể trả về được thì nó vẫn trả về bình thường. Nhưng nếu một thực thể của class Dude gửi một thông điệp mà nó không thể thực trả về được thông điệp đó thì nó sẽ gọi đến medthod_missing và trả về là 'radical haircut'. Thật là nột ý tưởng tồi để ghi đè method_missing cho tất cả các thông điệp. Vậy hãy refactor đoạn code trên để chỉ overwrite method_missing cho các thông điệp sau: :haircut, :flow, and :dome_style.
class Dude
def method_missing(name, *args)
return super unless [:haircut, :flow, :dome_style].include?(name)
'radical haircut'
end
end
p Dude.new.undefined_method # => NoMethodError: undefined method `undefined_method'
p Dude.new.haircut # => 'radical haircut'
Các phương thức methods() và respond_to?() sé không đăng kí các đối tượng thông điệp mới có thể sử lý khi message_missing được customized.
class A
def method_missing(name, *args)
return "hi" if name == :hello
end
end
a = A.new
p a.hello # => 'hi'
a.respond_to? :hello # => false
a.methods.include? :hello # => false
Khi method_missing được sử dụng method()s và respond_to?() cần được update luông vì vậy nó sẽ không trả về các câu trả lời không chính xác nữa.
Method_missing() có thể còn được sử dụng cho các công nghệ lập trình mạnh mẽ như tạo các đối tương mà thông điệp trả về với giá trị của các biến instance của nó.
class Person
def initialize
@first_name = 'bob'
@last_name = 'lob'
end
def method_missing(name, *args)
iv = "@#{name.to_s}"
if instance_variables.include?(iv.to_sym)
instance_variable_get(iv)
else
super
end
end
end
p = Person.new
# instance variables are accessible as methods
p.first_name # => "bob"
p.last_name # => "lob"
# Kernel#method_missing is still called for messages
# that don't correspond to instance variable names
p.phattie # => NoMethodError: undefined method `phattie'
# when new instance variables are defined, they are accessible
p.instance_variable_set(:@height, "six foot") # => "six foot"
p.height # => "six foot"
Method_missing() là một công nghệ lập trình ruby mạnh mẽ.
Link tham khảo
http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/ http://gshutler.com/2013/04/ruby-2-module-prepend/ http://rubyblog.pro/2016/08/module-prepend-in-ruby-2 https://codequizzes.wordpress.com/2014/04/24/rubys-method_missing/
All rights reserved