Module mixin và vấn đề đa thừa kế trong rails

Module mixin là gì?

Module mixin (mixin) hiểu một cách đơn giản thì nó là một module được include trong Class. Khi bạn include một Module vào trong một Class thì class đó sẽ có quyền truy cập và sử dụng các phương thức của Module.

Tác dụng của mixin là gì?

  1. Như các bạn đã biết Ruby không cho phép một class đa kế thừa từ nhiều class khác. Và để giải quyết vấn để này thì Module Mixin được đưa ra.
  2. Mixin có thể xem như interface trong các ngôn ngữ C#, Java… chỉ có điều các method của nó đã được định nghĩa.
  3. Tăng khả năng tái sử dụng code và giải quyết được vấn đế “diamond problem” trong multi-inheritance.

Cách sử dụng mixin

  • Bạn có một module Greetings như sau:
module Greetings
  def hello
    puts "Hello!"
  end
 
  def bonjour
    puts "Bonjour!"
  end
 
  def hola
    puts "Hola!"
  end
end
  • Bạn có một Class User muốn sử dụng các phương thức trong module Greetings thì việc bạn cần include vào để sử dụng, như sau:
class User
  include Greetings
end
  • Như vậy trên các thể hiện của lớp User bạn đã có thể sử dụng các phương thức trong module Greetings :
philip = User.new
philip.hola
=> Hola!
  • Tuy nhiên, nếu bạn gọi các phương thức trong module Greetings giống như một phương thức của Class thì bạn sẽ nhận được một thông báo lỗi.
User.hola
=> undefined method 'hola' for User:Class (NoMethodError)
  • Để khắc phục lỗi trên bạn chỉ cần thay từ khóa include bằng từ khóa extend:
class User
  extend Greetings
end
  • Bây giờ bạn đã có thể sử dụng:
User.hola!
=> Hola!
  • Nhưng bạn lại không thể gọi phương thức từ một thể hiện của class User
philip = User.new
philip.hola
=> undefined method 'hola' for #<User:0x007fbd5b9ae438> (NoMethodError)

Giải quyết vấn đề "diamond problem" trong đa thừa kế khi sử dụng mixin.

Trong lập trình hướng đối tượng với đa kế thừa, diamond problem là một vấn đề khá nổi tiếng khi mà : Có 2 class B và C kế thừa từ A, Class D kế thừa từ A và B. Khi gọi phương thức của A (không được override lại trong D) thì sẽ gọi tới hàm nào?

module B
    include A
   def hello
       “Hello from B”
    end
end

module C
    include A
    def hello
         “Hello from C”
    end
end

class D
include B
include C
end
D.new.hello # => “Hello from C”

Nếu chúng ta thay đổi thứ tự khi include thì kết quả sẽ thay đổi tương ứng:

class D
include C
include B
end
D.new.hello # => “Hello from B

Như vậy: Cứ module nào được include sau sẽ được tìm kiếm method của module đó trước đó là điều giải thích vì sao không diễn ra quá trình nhập nhằng khi tìm kiếm method trong 2 module B và A. Vậy, vấn đề "diamond problem" đã được giải quyết.

Kết luận

Mixins là giải pháp hoàn hảo khi bạn muốn chia sẻ chức năng giữa các lớp khác nhau. Thay vì lặp đi lặp lại cùng một mã, và hơn nữa, bạn có thể nhóm đơn giản các chức năng phổ biến vào một Module và sau đó đưa nó vào mỗi lớp khi bạn muốn sự dụng các chức năng đó.


All Rights Reserved