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

Inheritance vs Composition ใน C# - IS-A vs HAS-A

การออกแบบ 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)
CouplingHigh (Tight)Low (Loose)Low
FlexibilityCompile-timeRuntime (Swap Components)Runtime
Code ReuseWhite-box (เข้าถึง Protected)Black-box (ผ่าน Interface)Black-box
Life Cycleรวมกับ Baseรวมกับ Owner (Owner สร้าง/ทำลาย)แยกต่างหาก
PolymorphismBuilt-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);
    }
}