Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Decorator vs Subclass ใน Rails: เลือก Design Pattern ให้ถูกต้อง

Rails 5.1 เพิ่ม delegate_missing_to ให้ Forward Method ที่ไม่มีไป Object อื่น แทนที่จะสร้าง Subclass บทความนี้วิเคราะห์ว่าทำไม Decorator Pattern ถึงดีกว่า Inheritance บางกรณี

delegate_missing_to คืออะไร?

class Player
  delegate_missing_to :@user
  
  def initialize(user)
    @user = user
  end
  
  def points
    Game.points_for_user(user.id)
  end
end

Player.new(user).name  # เรียก user.name ผ่าน delegate_missing_to

Gavin คอมเมนต์ว่า: "ทำไมไม่สร้าง Subclass แทน? ได้ผลเหมือนกัน ไม่ต้องเพิ่ม Feature" → DHH ใน Pull Request อธิบายว่า: "นี่คือ Pattern ปกติสำหรับ Decorator"

ทำไมต้อง Decorator?

Decorator เปลี่ยนพฤติกรรม Object โดยไม่สร้าง Subclass

Player "Decorate" User → Player เกือบเหมือน User แต่มี Method เพิ่ม points ทำได้โดยไม่ใช้ Inheritance

เมื่อไหร่ควรใช้ Decorator?

กรณีที่ 1: สร้าง Object ครั้งเดียว ใช้เลย (Subclass ดีกว่า)


# Decorator - โค้ดมากกว่า
player = Player.new(User.new(name: "Justin"))

# Subclass - สั้นกว่า
player = Player.new(name: "Justin")

✅ ถ้าสร้างและใช้ในที่เดียว Subclass ดีกว่า Decorator แค่ซับซ้อน

กรณีที่ 2: เพิ่มพฤติกรรมภายหลัง ไกลจากจุดสร้าง (Decorator ดีกว่า)


user = User.find(1)
# ... โค้ดอื่นๆ ระยะไกล ...
player = Player.new(user)

✅ Decorator ช่วย Separate Behavior:

  • Code สร้าง User ไม่ต้องรู้ว่า Player มีอยู่
  • ยังใช้ User Object ตัวเดิมได้ ถ้าไม่ต้องการ Method เพิ่ม
  • แยก Behavior: Player, Employee, Developer แต่ละ Class ทำหน้าที่เดียว (Single Responsibility)

กรณีที่ 3: หลีกเลี่ยง Private Method Coupling

Subclass เรียก Private Method ของ Parent ได้ → แตกง่ายตอน Parent เปลี่ยน Implementation

Decorator เรียกได้แค่ Public Interface → Decoupled จาก Implementation Details

รวม Decorators ได้ (Composition)


# Player decorates User
# Admin decorates Player
# Auditor decorates Admin

player = Player.new(user)
admin = Admin.new(player)
auditor = Auditor.new(admin)

auditor.name      # → user.name (Delegate chain)
auditor.points    # → player.points
auditor.permissions # → admin.permissions

✅ แต่ละ Decorator ทำหน้าที่เดียว รวมกันได้ Complex Behavior

Ruby มีหลายวิธี Share Behavior

  • Subclass (Inheritance)
  • Modules (Mixin)
  • Delegate / Forwardable
  • Method Missing / SimpleDelegator
  • Decorator Pattern - ใช้ Instance Variable + Method Call (Building Blocks ของ OOP)

Decorator ให้ Flexible Behavior ระหว่าง Runtime โดยไม่ Overcomplicate Code

สรุป

สถานการณ์แนะนำ
Behavior คงที่ สร้างใช้ทันทีSubclass / Module Include
เพิ่ม Behavior ภายหลัง / DynamicDecorator
ต้องการแยก ResponsibilitiesDecorator
อยากหลีกเลี่ยง Parent Private Method CouplingDecorator
ต้อง Compose Multiple BehaviorsDecorator Chain