บทความนี้เกี่ยวกับ method delegation ใน Ruby
คุณจะเรียนรู้วิธีใช้เมธอด delegate, โมดูล Forwardable และคลาส SimpleDelegator
ทำไมเราต้องการ delegation?
ใน Object-Oriented Programming มีสองวิธีสำหรับคลาสทำงานร่วมกัน
คือ:
- Inheritance (การสืบทอด)
- Composition (การประกอบ)
ด้วย inheritance คุณสร้าง class hierarchies ที่ parent class แชร์เมธอด constants และ instance variable definitions ให้คลาสที่สืบทอด
ตัวอย่าง:
ใน Ruby ทุกออบเจกต์สืบทอดจากคลาส Object โดย default
นั่นคือเหตุผลที่คุณเข้าถึงเมธอดอย่าง puts, class และ object_id ได้
ด้วย composition คลาสสร้าง (หรือได้รับ) ออบเจกต์จากอีกคลาส… แล้วใช้ออบเจกต์เหล่านี้ delegate งานให้พวกมัน
ตัวอย่าง:
คอมพิวเตอร์ประกอบด้วยหลายส่วน (ออบเจกต์) และแต่ละส่วนรู้จักทำอย่างเดียวดี
หากอยาก render บนหน้าจอ คอมพิวเตอร์จะบอก graphics card ให้แสดงอะไร แต่ไม่บอกวิธีทำ
นั่นคือ composition!
แตถ้าคุณคือคอมพิวเตอร์ และอยากให้เข้าถึงเมธอดของ graphics card?
คุณต้องสร้าง "bridge" ระหว่างพวกมัน
มา看看วิธีทำ
Ruby Method Delegation — ตัวอย่าง
เรามีสองคลาส:
ComputerMemory
โค้ดที่กำหนด:
class Computer
def initialize
@memory = Memory.new
end
end
class Memory
def initialize
@data = []
end
def write(data)
@data << data
end
def read(index)
@data[index]
end
end
computer = Computer.new
เราอยากเข้าถึง memory ผ่าน Computer เท่านั้น
อาจมีหลายเหตุผล:
- คุณมี memory unit เดียว หมายถึงอยากมี memory object เดียว โดย centralize access ในออบเจกต์เดียว คุณบรรลุได้
- อยากควบคุมว่าใครเข้าถึง memory (แอปพลิเคชันต่างๆ) และส่วนไหนของ memory ที่เข้าถึงได้
- อยาก log ทุก memory access เพื่อ security หรือ debugging
ตอนนี้ หากลองทำนี้:
computer.write("rubyguides")
จะ fail ด้วย NoMethodError เพราะไม่มีเมธอด write บน Computer
undefined method `write' for #<Computer:0x000055c2e8f7d310> (NoMethodError)
คลาส Computer ไม่รู้ว่าคุณหมายถึงอะไรด้วย write
เว้นแต่…
คุณสร้างเมธอด write!
วิธีทำ:
class Computer
def initialize
@memory = Memory.new
end
def write(data)
@memory.write(data)
end
def read(index)
@memory.read(index)
end
end
เราส่ง request ต่อไปให้ออบเจกต์ @memory
นั่นคือ method delegation
Note: สิ่งนี้ทำให้เมธอดเหล่านี้เป็นส่วนของ public interface (พร้อมให้ทุกคนใช้) และคุณไม่ได้อยากอย่างนั้นเสมอไป
มี shortcut สำหรับ method delegation ไหม?
มีครับ!
มา看看…
วิธีใช้ Forwardable Module
ตอนนี้:
คุณใช้โมดูล Forwardable ที่มาพร้อม Ruby เพื่อนิยาม delegator methods
มันทำงานแบบนี้:
require 'forwardable'
class Computer
extend Forwardable
def_delegators :@memory, :read, :write
def initialize
@memory = Memory.new
end
end
สิ่งนี้ช่วยให้คุณลบเมธอดที่สร้างก่อนหน้านี้ออก และมันจะทำสิ่งเดียวกัน
Forwardable จะจัดการ method arguments (รวมถึง blocks!) ให้คุณ ไม่ต้องกังวลเรื่องนั้น
สะดวกมากใช่ไหม? 🙂
วิธีใช้ Rails Delegate Method
หากคุณใช้ Rails หรือ ActiveSupport gem เพียงอย่างเดียว คุณเข้าถึงเมธอด delegate ได้
มันทำงานแบบนี้:
class Computer
delegate :read, :write, to: :@memory
def initialize
@memory = Memory.new
end
end
เมธอด delegate รับอาร์กิวเมนต์ prefix ซึ่งอนุญาตให้คุณเพิ่ม prefix ให้ชื่อเมธอด
ตัวอย่าง:
class Computer
delegate :read, :write, prefix: "memory", to: :@memory
def initialize
@memory = Memory.new
end
end
ผลลัพธ์เป็นสองเมธอด:
memory_readmemory_write
วิธี Delegate ทุกอย่างด้วย SimpleDelegator
เทคนิคที่เรียนรู้ไปใช้ forward หรือ delegate เมธอดเฉพาะ
แต่หากอยาก wrap ออบเจกต์ และ expose ทุกเมธอดของมัน…
คุณใช้คลาส SimpleDelegator!
วิธีทำ:
require 'delegate' class CoolArray < SimpleDelegator end cool = CoolArray.new([]) cool << "ruby" cool << "gems" p cool
ตอนนี้ CoolArray พฤติกรรมเหมือนออบเจกต์ที่คุณส่งให้ new
ทำไมถึงมีประโยชน์?
คุณเพิ่มเมธอดใหม่ให้ออบเจกต์นี้ได้ โดยไม่ต้องเปลี่ยนคลาสต้นฉบับ
วิดีโอสอน
สรุป
คุณเรียนรู้เกี่ยวกับ object composition ใน Ruby และวิธี delegate เมธอดด้วยเทคนิคต่างๆ แล้ว!
หากเพลิดเพลินกับบทความนี้กรุณา แชร์ ให้เพื่อน Ruby ของคุณ
ขอบคุณที่อ่าน 🙂