Delegation ง่ายๆ บน Ruby

แนวคิดเรื่อง การเลือกใช้composition ก่อน inheritance เป็นที่พูดถึงกันมานานพอสมควรแล้ว ผมได้ยินแนวคิดนี้เป็นครั้งแรกจากบทหนึ่งในหนังสือ Effective Java และก็ยังคงได้ยินผลของแนวคิดนี้มาจนถึงปัจจุบันตัวอย่าง เช่น ภาษาที่เกิดในยุคหลังนี้อย่าง Go ที่จะไม่มี inheritanceให้ใช้เลย

โดยปกติแล้วเราจะชอบใช้ inheritance กันมากกว่าเพราะมันเขียนสั้นกว่า แค่เพียง extend คลาสแม่ ก็พร้อมใช้งานแล้ว ต่างจาก composition ที่ต้องมีการเขียนเพิ่มเติมอย่างชัดเจนว่าเราจะเรียกใช้อะไรจากคลาสต้นทาง ถ้าคลาสต้นทางมี method เยอะก็ต้องเขียนกันยาวเลยทีเดียว ใน blog นี้เรามาดูกันว่า Ruby มีตัวช่วยอะไรที่ทำให้การเขียน composition สั้นลงมากๆ

ก่อนอื่นผมขออธิบายคำศัพท์เล็กน้อย

  • composition คือ object หนึ่งมีการเรียกใช้ object อื่นๆ ภายใน
  • decorator เป็นหนึ่งใน Design Patterns ของ GOF ที่พูดถึง การที่ object หนึ่งเพิ่มความสามารถให้กับอีก object หนึ่ง โดยการห่อ object ต้นทางไว้ และยังคงความสามารถของต้นทางไว้ทั้งหมด
  • delegation คือ การที่ object หนึ่งส่งต่องานของตัวเองไปสู่อีก object หนึ่ง ซึ่งมักจะใช้เป็นคำพูดแบบรวมๆ ของทั้ง composition และ decorator

class X
  def xx
    'xx'
  end

  def xx2
    'xx2'
  end
end

class Y
  def yy(text)
    "yy#{text}"
  end
end

# composition
class A
  def initialize(x, y)
    @x = x
    @y = y
  end

  def a_xx
    @x.xx
  end

  def yy(text)
    @y.yy(text)
  end

  def aa
    yy(a_xx)
  end
end

a = A.new(X.new, Y.new)
puts a.a_xx        # => 'xx'
puts a.yy('z')     # => 'yyz'
puts a.aa          # => 'yyxx'
puts a.xx2         # => error!

# decorator
class XX
  def initialize(x)
    @x = x
  end

  def xx
    @x.xx
  end

  def xx2
    @x.xx2
  end

  def xx3
    "new method"
  end
end

xx = XX.new(X.new)
puts xx.xx   # => 'xx'
puts xx.xx2  # => 'xx2'
puts xx.xx3  # => 'new method'

SimpleDelegator ทำให้ decorator นั้นแสนสั้น

require 'delegate'

class XX < SimpleDelegator
  def xx3
    "new method"
  end
end

xx = XX.new(X.new)
puts xx.xx    # => 'xx'
puts xx.xx2   # => 'xx2'
puts xx.xx3   # => 'new method'

Forwardable ใช้กับ composition ได้ทั้งกรณีมี object ต้นทางหลาย object และไม่ต้องการจะเปิดความสามารถทุกอย่างให้ผู้ใช้ object ปลายทางเรียกใช้

require 'forwardable'

class A
  extend Forwardable

  def_delegator :@x, :xx, :a_xx # delegate แบบเปลี่ยนชื่อ a.a_xx => x.xx
  def_delegators :@y, :yy      # ลิสของ method ที่จะ delegate แบบไม่เปลี่ยนชื่อ

  def initialize(x, y)
    @x = x
    @y = y
  end

  def aa
    yy(a_xx)
  end
end

a = A.new(X.new, Y.new)
puts a.a_xx    # => 'xx'
puts a.yy('z') # => 'yyz'
puts a.aa      # => 'yyxx'
puts a.xx2     # => error!
Advertisements

One thought on “Delegation ง่ายๆ บน Ruby

  1. Pingback: Delegation ง่ายๆ บน Ruby | ruby_66

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s