การออกแบบ Class Relationship สองหลัก: Inheritance (IS-A) และ Composition (HAS-A)
Inheritance (การสืบทอด) - IS-A Relationship
Derived Class "เป็นประเภทหนึ่งของ" Base Class
using System;
// Base Class
public abstract class Animal
{
public string Name { get; set; }
public abstract void MakeSound();
}
// Derived Classes
public class Dog : Animal
{
public override void MakeSound() => Console.WriteLine($"{Name}: Woof!");
}
public class Cat : Animal
{
public override void MakeSound() => Console.WriteLine($"{Name}: Meow!");
}
// Usage
var animals = new Animal[] { new Dog { Name = "Buddy" }, new Cat { Name = "Kitty" } };
foreach (var a in animals) a.MakeSound();
Composition (การประกอบ) - HAS-A Relationship
Class "มี" อีก Class หนึ่งเป็นส่วนประกอบ Life Cycle ผูกมากกัน
public class Engine
{
public int HorsePower { get; set; }
public void Start() => Console.WriteLine("Engine started");
}
public class Car
{
// Composition: Car HAS-A Engine (Engine เป็นส่วนประกอบ)
private readonly Engine _engine = new Engine(); // สร้างภายใน
public int HorsePower => _engine.HorsePower;
public void Start()
{
_engine.Start();
Console.WriteLine("Car started");
}
}
// Usage
var car = new Car();
car.Start(); // Engine ก็ Start ตาม
Aggregation (Weak HAS-A) - ต่างจาก Composition
Object "มี" อีก Object แต่ Life Cycle แยกกัน (Shared Reference)
// Aggregation: Driver ไม่ได้เป็นส่วนของ Car, แยกกันได้
public class Driver { public string Name { get; set; } }
public class Car
{
public Driver Driver { get; set; } // Reference - ไม่ได้ Own
}
// Usage
var driver = new Driver { Name = "John" };
var car1 = new Car { Driver = driver };
var car2 = new Car { Driver = driver }; // Same Driver!
ตารางเปรียบเทียบ
| ด้าน | Inheritance (IS-A) | Composition (HAS-A) | Aggregation (Weak HAS-A) |
|---|---|---|---|
| ความสัมพันธ์ | "เป็นประเภทของ" | "มีเป็นส่วนประกอบ" | "มี/ใช้" (Reference) |
| Coupling | High (Tight) | Low (Loose) | Low |
| Flexibility | Compile-time | Runtime (Swap Components) | Runtime |
| Code Reuse | White-box (เข้าถึง Protected) | Black-box (ผ่าน Interface) | Black-box |
| Life Cycle | รวมกับ Base | รวมกับ Owner (Owner สร้าง/ทำลาย) | แยกต่างหาก |
| Polymorphism | Built-in (Virtual/Override) | ผ่าน Interface | ผ่าน Interface |
Design Principle: "Favor Composition over Inheritance"
GoF Design Patterns แนะนำให้เลือก Composition ก่อน เพราะ:
- ยืดหยุ่นกว่า (Runtime Behavior Change)
- Testable (Mock Dependencies)
- Single Responsibility Principle
- หลีกเลี่ยง Fragile Base Class Problem
- Multiple Implementations (Strategy Pattern)
เมื่อไหร่ใช้ Inheritance
- IS-A Relationship จริงๆ (Liskov Substitution Principle)
- ต้องการ Polymorphism ผ่าน Base Class/Interface
- Share Implementation (Template Method Pattern)
- Framework Design (Base Controllers, Base Entities)
เมื่อไหร่ใช้ Composition
- HAS-A Relationship
- ต้องการ Swap Behavior Runtime (Strategy, Plugin)
- Decouple Implementation
- Cross-cutting Concerns (Logging, Caching - Decorator Pattern)
ตัวอย่าง Strategy Pattern (Composition over Inheritance)
// Behavior Interface
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Concrete Strategies
public class CreditCardPayment : IPaymentStrategy { /* ... */ }
public class PayPalPayment : IPaymentStrategy { /* ... */ }
public class CryptoPayment : IPaymentStrategy { /* ... */ }
// Context ใช้ Composition
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal amount)
{
_paymentStrategy?.Pay(amount);
}
}