Advanced Modules in Ruby
Như các bạn đã biết, Module là một tập hợp các method, class và constant. Module mang lại 2 lợi ích chủ yếu: Cung cấp một namespace và tránh việc trùng lặp tên Thực hiện mixindễ dàng Module thực hiện mixin bằng cách sử dụng method include để include các method của nó vào trong một class, ...
Như các bạn đã biết, Module là một tập hợp các method, class và constant. Module mang lại 2 lợi ích chủ yếu:
- Cung cấp một namespace và tránh việc trùng lặp tên
- Thực hiện mixindễ dàng
Module thực hiện mixin bằng cách sử dụng method include để include các method của nó vào trong một class, khi đó, class này sẽ có khả năng truy cập vào các instance method bên trong module đó. Hãy xem một ví dụ sau đây để thấy rõ hơn về điều đó:
module Animal def say puts "Hello world!" end end class Cat include Animal end cat = Cat.new cat.say # Hello world!
Sau đây, tôi sẽ giới thiệu về một số kĩ thuật nâng cao khi sử dụng module.
Included callback
included là một callback mà Ruby gọi nó mỗi khi một module được include vào một module/class khác. Chúng ta cùng đến với một ví dụ như sau:
module Animal def self.included(base) puts "Animal has been included in class #{base}" end end class Cat include Animal end
Output:
Foo has been included in class Bar
Chú ý rằng, included được định nghĩa ở trong module sử dụng keyword self, với tham số là base - target class.
Extend method
Như đã trình bày ở trên, include có khả năng chia sẻ hành vi thông qua các module bằng cách mixin các methods từ một module vào một class.
Tuy nhiên, include có một hạn chế: nó chỉ có thể thêm instance method cho class chứ không thêm được class method. Cùng xem một ví dụ sau đây:
module Animal def self.say puts "Hello world!" end end class Cat include Animal end Cat.say
Output:
NoMethodError: undefined method `say' for Cat:Class
Như các bạn có thể thấy, chúng ta nhận được NoMethodError khi gọi method của module từ class.
Nhưng có những trường hợp bạn sẽ muốn include một vài method từ một module như một class method vào trong một class. Đó là khi bạn sẽ thấy extend trở nên hữu dụng (yeah).
extend method làm việc tương tự như include, nhưng không giống như include, bạn có thể sử dụng nó để extend bất cứ object nào bằng cách thêm các method và các constant từ một module. Ví dụ như sau:
module Animal def say puts "Hello world!" end end class Cat end cat = Cat.new cat.extend Animal cat.say
Output:
Hello world!
Điều đáng chú ý ở đây là extend làm việc ở mọi nơi: bên trong một class/module và trên các instance cụ thể. Còn include thì không thể sử dụng được trên các instance cụ thể. Như ví dụ trên thì cat.include Animal sẽ raise ra một lỗi NoMethodError: undefined method 'include' for #<Cat:0x0000000d880ad8>.
Theo một nghĩa nào đó, extend có thể được sử dụng để bắt chước chức năng của method include. Khi bạn thêm một câu lệnh include vào trong một class, Ruby đảm bảo rằng tất cả những instance của class đó sẽ có những method được định nghĩa bên trong module đã được include.
Tuy nhiên, bạn không nên sử dụng phương pháp extend này để thay thế chức năng thuần túy của include là mixin các method từ một module vào một class mà nên sử dụng include vào mục đích này. ^^
Sử dụng extend để thêm class method vào class
Như đã đề cập ở trên, extend còn có khả năng thêm các class method từ một module vào một class - điều mà include không thể làm được.
Hãy xem một ví dụ sau đây:
module Animal def say_hi puts "Hi!" end end class Cat end Cat.extend Animal Cat.say_hi
Output:
Hi!
Như vậy ta có thể thấy rằng, bạn có thể extend một instance của một class hoặc extend chính class đó.
Extended callback
Cũng giống như included là một callback cho method include, đây là một callback cho method extend. Tương tự, nó được gọi mỗi khi một module được extend vào một module/class khác. Cùng xem một ví dụ sau đây để hiểu rõ hơn:
module Animal def self.extended(base) puts "Class #{base} has been extended with module #{self} !" end end class Cat extend Animal end
Output:
Class Cat has been extended with module Animal !
Trên đây mình đã giới thiệu qua về một số kĩ thuật nâng cao khi sử dụng module, hi vọng nó sẽ giúp ích cho bạn đọc trong quá trình làm việc với Ruby (yeah)