Technology

composition oop คือ

composition oop คอ
composition oop คือ | SiamCafe Blog
2025-07-16· อ. บอม — SiamCafe.net· 9,792 คำ

Composition OOP

Composition OOP Has-a Relationship Composition over Inheritance Dependency Injection Design Patterns Loose Coupling Flexibility Runtime Behavior Strategy Decorator

ConceptRelationshipCouplingFlexibilityเหมาะกับ
InheritanceIs-aTightต่ำTrue Hierarchy
CompositionHas-aLooseสูงทั่วไป
AggregationHas-a (weak)LooseสูงOptional Parts
InterfaceCan-doVery LooseสูงมากContract
MixinMixed-inปานกลางปานกลางShared Behavior

Composition vs Inheritance

# === Composition vs Inheritance ===

# BAD: Deep Inheritance Hierarchy
# class Animal:
#     def eat(self): ...
#
# class Mammal(Animal):
#     def breathe(self): ...
#
# class Dog(Mammal):
#     def bark(self): ...
#
# class GuideDog(Dog):
#     def guide(self): ...
#
# Problem: GuideDog สืบทอด 4 ชั้น เปลี่ยน Animal กระทบทุก Class

# GOOD: Composition
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Protocol

# Define Behaviors as Protocols (Interfaces)
class Movable(Protocol):
    def move(self) -> str: ...

class Soundable(Protocol):
    def make_sound(self) -> str: ...

class Trainable(Protocol):
    def perform_task(self, task: str) -> str: ...

# Implement Behaviors as separate classes
class WalkBehavior:
    def move(self) -> str:
        return "Walking on 4 legs"

class FlyBehavior:
    def move(self) -> str:
        return "Flying with wings"

class SwimBehavior:
    def move(self) -> str:
        return "Swimming with fins"

class BarkSound:
    def make_sound(self) -> str:
        return "Woof!"

class MeowSound:
    def make_sound(self) -> str:
        return "Meow!"

class GuideTraining:
    def perform_task(self, task: str) -> str:
        return f"Guiding: {task}"

class SearchTraining:
    def perform_task(self, task: str) -> str:
        return f"Searching for: {task}"

# Compose behaviors into Animal
@dataclass
class Animal:
    name: str
    movement: Movable
    sound: Soundable
    training: Trainable = None

    def describe(self):
        desc = f"{self.name}: {self.movement.move()} | {self.sound.make_sound()}"
        if self.training:
            desc += f" | Can: {self.training.perform_task('demo')}"
        return desc

# Create animals with different compositions
dog = Animal("Dog", WalkBehavior(), BarkSound())
guide_dog = Animal("Guide Dog", WalkBehavior(), BarkSound(), GuideTraining())
search_dog = Animal("Search Dog", WalkBehavior(), BarkSound(), SearchTraining())
cat = Animal("Cat", WalkBehavior(), MeowSound())

animals = [dog, guide_dog, search_dog, cat]
print("=== Composition Example ===")
for a in animals:
    print(f"  {a.describe()}")

# Runtime behavior change!
print("\n  Dog learns to swim:")
dog.movement = SwimBehavior()
print(f"  {dog.describe()}")

Design Patterns

# === Strategy Pattern (Composition) ===

class PaymentStrategy(Protocol):
    def pay(self, amount: float) -> str: ...

class CreditCardPayment:
    def __init__(self, card_number: str):
        self.card = card_number[-4:]
    def pay(self, amount: float) -> str:
        return f"Paid  with Credit Card ****{self.card}"

class PayPalPayment:
    def __init__(self, email: str):
        self.email = email
    def pay(self, amount: float) -> str:
        return f"Paid  via PayPal ({self.email})"

class CryptoPayment:
    def __init__(self, wallet: str):
        self.wallet = wallet[:8]
    def pay(self, amount: float) -> str:
        return f"Paid  with Crypto ({self.wallet}...)"

@dataclass
class ShoppingCart:
    items: list = field(default_factory=list)
    payment: PaymentStrategy = None

    def add_item(self, name: str, price: float):
        self.items.append({"name": name, "price": price})

    def total(self) -> float:
        return sum(item["price"] for item in self.items)

    def checkout(self) -> str:
        if not self.payment:
            return "Error: No payment method set"
        return self.payment.pay(self.total())

cart = ShoppingCart()
cart.add_item("Laptop", 999.99)
cart.add_item("Mouse", 29.99)

# Strategy: เปลี่ยน Payment Method ตอน Runtime
cart.payment = CreditCardPayment("4111111111111234")
print(f"\n=== Strategy Pattern ===")
print(f"  {cart.checkout()}")

cart.payment = PayPalPayment("user@example.com")
print(f"  {cart.checkout()}")

cart.payment = CryptoPayment("0x1234567890abcdef")
print(f"  {cart.checkout()}")

# === Decorator Pattern (Composition) ===
class Logger:
    def __init__(self, wrapped):
        self._wrapped = wrapped
    def pay(self, amount: float) -> str:
        print(f"  [LOG] Processing payment of ")
        result = self._wrapped.pay(amount)
        print(f"  [LOG] Payment result: {result}")
        return result

print(f"\n=== Decorator Pattern ===")
logged_payment = Logger(CreditCardPayment("9876543210001234"))
logged_payment.pay(1029.98)

Dependency Injection

# === Dependency Injection with Composition ===

# Database Repository (Interface)
class UserRepository(Protocol):
    def find_by_id(self, user_id: int) -> dict: ...
    def save(self, user: dict) -> bool: ...

# Concrete Implementations
class PostgresUserRepo:
    def find_by_id(self, user_id: int) -> dict:
        return {"id": user_id, "name": "John", "source": "PostgreSQL"}
    def save(self, user: dict) -> bool:
        return True

class MongoUserRepo:
    def find_by_id(self, user_id: int) -> dict:
        return {"id": user_id, "name": "John", "source": "MongoDB"}
    def save(self, user: dict) -> bool:
        return True

class InMemoryUserRepo:
    """For testing"""
    def __init__(self):
        self.users = {}
    def find_by_id(self, user_id: int) -> dict:
        return self.users.get(user_id, {})
    def save(self, user: dict) -> bool:
        self.users[user["id"]] = user
        return True

# Service uses Composition + DI
@dataclass
class UserService:
    repo: UserRepository  # Injected dependency

    def get_user(self, user_id: int) -> dict:
        return self.repo.find_by_id(user_id)

    def update_user(self, user_id: int, name: str) -> bool:
        user = self.repo.find_by_id(user_id)
        user["name"] = name
        return self.repo.save(user)

# Production: ใช้ Postgres
prod_service = UserService(repo=PostgresUserRepo())
print(f"\n=== Dependency Injection ===")
print(f"  Production: {prod_service.get_user(1)}")

# Testing: ใช้ InMemory
test_repo = InMemoryUserRepo()
test_repo.save({"id": 1, "name": "Test User"})
test_service = UserService(repo=test_repo)
print(f"  Testing: {test_service.get_user(1)}")

# Switch to Mongo: เปลี่ยน Implementation ไม่ต้องแก้ Service
mongo_service = UserService(repo=MongoUserRepo())
print(f"  MongoDB: {mongo_service.get_user(1)}")

# Benefits Summary
benefits = {
    "Loose Coupling": "Service ไม่ผูกกับ Database ใดๆ",
    "Testability": "Mock/InMemory Repository สำหรับ Test",
    "Flexibility": "เปลี่ยน Database ไม่ต้องแก้ Business Logic",
    "Single Responsibility": "แต่ละ Class ทำหน้าที่เดียว",
    "Open/Closed": "เพิ่ม Implementation ใหม่ไม่ต้องแก้ Code เดิม",
}

print(f"\n  Composition Benefits:")
for k, v in benefits.items():
    print(f"    [{k}]: {v}")

เคล็ดลับ

Composition ใน OOP คืออะไร

สร้าง Object รวม Object อื่น Has-a Relationship แทน Inheritance ยืดหยุ่น เปลี่ยน Runtime Loose Coupling Test ง่าย Mock ง่าย

Composition ต่างจาก Inheritance อย่างไร

Inheritance Is-a Tight Coupling Parent กระทบ Child Composition Has-a Loose Coupling เปลี่ยนอิสระ Swap Runtime Test ง่าย Code มากกว่า

เมื่อไหร่ควรใช้ Composition เมื่อไหร่ใช้ Inheritance

Inheritance True Is-a ไม่เปลี่ยน Runtime Composition Flexibility Cross-cutting Multiple Behaviors ส่วนใหญ่ Composition ดีกว่า

Design Pattern ที่ใช้ Composition มีอะไรบ้าง

Strategy เปลี่ยน Algorithm Decorator เพิ่ม Behavior Observer แจ้งเตือน Composite Tree Command Action Adapter Interface Builder Object

สรุป

Composition OOP Has-a Relationship Composition over Inheritance Strategy Decorator Dependency Injection Protocol Interface Loose Coupling Testability Production Code

📖 บทความที่เกี่ยวข้อง

Vue Composition API Production Setup Guideอ่านบทความ → hierarchy oop คืออ่านบทความ → oop abstract คืออ่านบทความ → method oop คืออ่านบทความ → javascript functional programming vs oopอ่านบทความ →

📚 ดูบทความทั้งหมด →