Tìm hiểu sâu về Ruby Modules 2
Nối tiếp phần trước (https://viblo.asia/march_vu/posts/jvElaLgYZkw), bài viết này tôi sẽ tìm hiểu kĩ về các phương thức Including , Prepending và Extending trong Ruby Object Model (ROM) . Including Hãy bắt đầu mở class Car và include vào nó một Module: module V12Engine def ...
Nối tiếp phần trước (https://viblo.asia/march_vu/posts/jvElaLgYZkw), bài viết này tôi sẽ tìm hiểu kĩ về các phương thức Including, Prepending và Extending trong Ruby Object Model (ROM).
Including
Hãy bắt đầu mở class Car và include vào nó một Module:
module V12Engine def start; "...roar!"; end end class Car include V12Engine end
Khi chúng ta include một module trong class, Ruby đặt nó trực tiếp phía trên class. Điều này có nghĩa rằng khi chúng ta gọi một phương thức từ object, Ruby sẽ tìm nó trong hệ thống object và chạy nó:
my_car.start => ...roar!"
Đây là cách thường lệ để kế thừa trong Ruby, chúng ta có thể include nhiều module trong class, để thêm nhiều chức năng bổ sung.
Chú ý: Nếu chúng ta định nghĩa một method có tên trùng với method trong class Car, Ruby sẽ tìm và chạy method đó thay thế, method trong Module sẽ không bao giờ được gọi.
Prepending
Khi chúng ta prepend một module cho class, Ruby đặt nó trực tiếp dưới class. Có nghĩa rằng, trong khi tra cứu method, Ruby sẽ đếm các method được định nghĩa trong Module trước các method được định nghĩa trong Class. Kết quả, bất kỳ các method của module sẽ thay thế các method trong class khi tên giống nhau. Hãy thử điều này:
module ElectricEngine def drive; "eco-driving!"; end end class Car prepend ElectricEngine end my_car = Car.new my_car.drive => eco-driving!
Quan sát rằng nếu chúng ta gọi một method drive trong my_car, nó sẽ nhận method trong module, thay vì trong class Car. Prepending modules sẽ rất hữu dụng khi chúng ta muốn phạm vi logic method phù hợp với điều kiện biến đổi bên ngoài. Ví dụ, chúng ta muốn đánh giá sự thể hiện của method khi chạy trong môi trường test. Thay vì làm rối các method với lệnh if và code, chúng ta có thể thêm các method đặc biệt trong module riêng và chỉ thêm vào trước module tới class của chúng ta nếu chúng ta trong môi trường test:
module Instrumentable require 'objspace' def drive puts ObjectSpace.count_objects[:FREE] super puts ObjectSpace.count_objects[:FREE] end end class Car prepend Instrumentable if ENV['RACK_ENV'] = 'test' end my_car = Car.new.drive => 711 => driving => 678
Including và Prepending vào Instances
Các instance object được đặt phía ngoài hệ thống phân cấp ROM, không có ‘trên’ hay ‘dưới’ của chúng, nơi chúng ta có thể đặt các module. Điều này có nghĩa chúng ta không thể include hay prepend các module tới các object cụ thể.
Extending Classes
Khi chúng ta extend class object với một module, Ruby đặt nó trực tiếp phía trên eigenclass object của chúng ta:
module SuperCar def fly; "flying!"; end end class Car extend SuperCar end
Các instances Car của chúng ta không thể truy cập đến method fly, phương thức tra cứu sẽ bắt đầu tại instance car, my_car, di chuyển bên phải tới eigenclass my_car và sau đó lên tới Car class và nó là nguồn gốc. method fly sẽ không bao giờ được tìm thấy trong đường dẫn đó:
my_car.fly => NoMethodError: undefined method `fly' for #<Car:0x000000019ae8d0>
Nhưng nếu ta gọi method trong class object thay thế?
Car.fly => flying
Object nhận của chúng ta bây giờ chính bản thân class Car. Đầu tiên Ruby sẽ nhìn bên phải (Car) và sau đó xem module SuperCar ở đâu, nó sẽ tìm thấy các phương thức fly.
Extending Instances
Chúng ta có thể extend một instance đặc biệt, như sau:
my_car.extend(SuperCar)
Việc này sẽ đặt module SuperCar phía trên eigen-class của object.
Bây giờ chúng ta có thể gọi method fly ở my_car:
my_car.fly => flying
Nhưng không gọi được ở another_car:
another_car.fly => NoMethodError: undefined method `fly' for #<Car:0x000000012ae868>
Extending một instance object với một Module là một cách tự động để decorate nhưng Class instances đặc biệt.
The end! Thank you!
Tài liệu dịch: https://www.sitepoint.com/get-the-low-down-on-ruby-modules/