Module Forwardable trong Ruby
Trong bài viết này chúng ta sẽ tìm hiểu: Forwardable module def_delegator method def_delegators method delegate method The Forwardable module Forwardable là một module có thể được sử dụng để thêm các hành vi vào tất cả các đối tượng của một lớp cụ thể. Module này được included vào ...
Trong bài viết này chúng ta sẽ tìm hiểu:
- Forwardable module
- def_delegator method
- def_delegators method
- delegate method
The Forwardable module
Forwardable là một module có thể được sử dụng để thêm các hành vi vào tất cả các đối tượng của một lớp cụ thể.
Module này được included vào các singleton class bằng cách sử dụng từ khóa extend để thêm các phương thức ở class-level (để giữ được sự đơn giản)
def_delegator method
Forwardable#def_delegator method cho phép một đối tượng chuyển tiếp một message tới một receiver.
Các bạn có thể đọc thêm về message và receiver tại đây
Không có gì tốt hơn ví dụ để làm sáng tỏ 1 vấn đề. Chúng ta cùng đến ví dụ nhé ^^
# in forwardable.rb class Hero attr :skills def initialize @skills = [:strong, :keen, :brave] end end jack = Hero.new puts "Jack's main skill: #{jack.skills.first}"
Kết quả:
?> ruby forwardable.rb Jack's main skill: strong
Đương nhiên là đoạn code này hoạt động, nhưng gọi jack.skills.first bên ngoài lớp Hero có chút bất thường...
Vì vậy, hãy đóng gói đoạn code này vào trong lớp Hero
# in forwardable.rb class Hero attr :skills def initialize @skills = [:strong, :keen, :brave] end def main_skill @skills.first end end jack = Hero.new puts "Jack's main skill: #{jack.main_skill}"
Kết quả:
?> ruby forwardable.rb Jack's main skill: strong
Tốt hơn rồi đó! Ở đây method Hero#main_skill chứa logic để truy cập vào chưởng chính của hero ^^
Giải pháp này có thể chấp nhận được. Nhưng Ruby cung cấp một cơ chế chuyển tiếp message(#first) từ một object(jack) đến một receiver(skills) bằng cách sử dụng Forwardable#def_delegator method.
Chúng ta cùng sửa đoạn code trên bằng phương pháp này nhé!
# in forwardable.rb require 'forwardable' class Hero attr :skills extend Forwardable def_delegator :@skills, :first, :main_skill def initialize @skills = [:strong, :keen, :brave] end end jack = Hero.new puts "Jack's main skill: #{jack.main_skill}"
Kết quả
?> ruby forwardable.rb Jack's main skill: strong
Cool! Ở đây chúng ta đã tránh tạo phương thức getter để truy cập vào skills.first bằng cách sử dụng hệ thống message forwarding do Ruby cung cấp.
Đầu tiên, chúng ta require thư viện forwardable.
Sau đó, chúng ta extend Forwardable để thêm các phương thức của module ở class-level.
Sau đó chúng ta sử dụng phương thức def_delegator mới được thêm vào:
- Đối số đầu tiên là @skills tương ứng với receiver của message forwarding
- Đối số thứ hai là :first là message forwarding
- Và cuối cùng là đối số thứ ba main_skill là bí danh của :first message. Nên khi gọi jack.main_skill - dễ đọc hơn jack.first - sau đó skills.first sẽ được gọi tự động.
def_delegators method
Sự khác biệt giữa phương thức này với phương thức def_delegator là nó lấy một tập các phương thức để chuyển tiếp và các phương thức không thể đặt bí danh.
# in forwardable.rb require 'forwardable' class Todolist attr :tasks extend Forwardable def_delegators :@tasks, :first, :last def initialize @tasks = %w[conception implementation refactoring] end end todolist = Todolist.new puts "first tasks: #{todolist.first}" puts "last tasks: #{todolist.last}"
Kết quả:
?> ruby forwardable.rb first tasks: conception last tasks: refactoring
Ở đây, phương thức first và last của mảng tasks có thể dùng cho mọi đối tược của Todolist. Khi một trong 2 phương thức này được gọi thì message sẽ được chuyển tới mảng tasks
delegate method
Phương thức delegate chấp nhận hash như đối số:
- key là một hoặc nhiều message
- value là receiver của message được định nghĩa ở key
# in forwardable.rb require 'forwardable' class Computer attr :cores, :screens extend Forwardable delegate %I[size] => :@cores, %I[length] => :@screens def initialize @cores = (1..8).to_a @screens = [1, 2] end end macrosoft = Computer.new puts "Cores: #{macrosoft.size}" puts "Screens: #{macrosoft.length}"
Kết quả:
$> ruby forwardable.rb Cores: 8 Screens: 2
Ở đây, macrosoft.size message tương ứng với macrosoft.cores.size. Và, macrosoft.length message tương ứng với macrosoft.screens.length
Thank for reading!
Nguồn: The Forwardable module in Ruby