Advanced Design Patterns

บทนำ: Design Patterns คืออะไร และทำไมสำคัญ

ในการพัฒนาซอฟต์แวร์ นักพัฒนามักเจอปัญหาเดียวกันซ้ำ ๆ และพบว่าวิธีแก้ปัญหาที่ดี สามารถนำมาใช้ใหม่ได้ในหลาย ๆ โปรเจกต์ Design Pattern คือการรวมรวมประสบการณ์เหล่านั้น เป็นแบบแผนที่พยายามแก้ปัญหาทั่วไป ที่ผ่านการทดสอบและปรับปรุงมาแล้ว

ตัวอย่างของปัญหาที่ design patterns แก้:

  • ต้องสร้าง object แต่ไม่รู้ class ไหนจะใช้ (Creational patterns)
  • ต้องลดการพึ่งพา (coupling) ระหว่าง classes (Structural patterns)
  • ต้องเรียกใช้ method ของ objects ที่แตกต่างกัน (Behavioral patterns)

เหตุผลที่ design patterns สำคัญ:

  1. Communication – นักพัฒนาคุยกันแล้วบอก “ใช้ Singleton” หรือ “ใช้ Factory” ทุกคนเข้าใจ
  2. Reusability – solution ที่ proven ใช้ได้ผ่านการทดสอบหลาย projects
  3. Maintainability – code ที่ follow patterns ง่ายต่อการบำรุงรักษา
  4. Flexibility – patterns ช่วยให้ code ยืดหยุ่น และ extensible

Creational Patterns: สร้าง Objects อย่างชาญฉลาด

ปัญหา: Complex Object Creation

ลองนึกสถานการณ์: เราต้องสร้าง database connection ซึ่งต้องการ:

  • ถ้า database type คือ MySQL – ใช้ MySQLConnection
  • ถ้า database type คือ PostgreSQL – ใช้ PostgreSQLConnection
  • ต้องมี configuration, authentication, connection pooling

ถ้า code ใช้ direct instantiation มันจะ messy:

java// ❌ Bad: Tight coupling and complexity
public class DataService {
    public void connectToDatabase(String dbType) {
        if (dbType.equals("mysql")) {
            Connection conn = new MySQLConnection();
            conn.initialize("localhost", 3306, "root", "password");
            conn.setupConnectionPool(10);
        } else if (dbType.equals("postgresql")) {
            Connection conn = new PostgreSQLConnection();
            conn.initialize("localhost", 5432, "postgres", "password");
            conn.setupConnectionPool(10);
        }
        // More complex logic...
    }
}

ปัญหา:

  • Tight coupling – DataService รู้เกี่ยวกับ MySQLConnection, PostgreSQLConnection
  • Hard to extend – เพิ่ม database type ใหม่ต้องแก้ DataService
  • Hard to test – ไม่สามารถ mock connections ได้ง่าย
  • Scattered logic – configuration logic กระจายไปหลายที่

ตัวอย่างที่ 1: Factory Pattern

Abstract Factory และ Concrete Factories

Factory Pattern คือการมีคน (factory) ที่จำเพาะในการสร้าง objects แล้ว return ให้คุณ แทนที่คุณสร้างเอง:

java// Step 1: สร้าง interface สำหรับ database connections
public interface DatabaseConnection {
    void connect(String host, int port, String username, String password);
    void executeQuery(String sql);
    void disconnect();
}

// Step 2: สร้าง concrete implementations
public class MySQLConnection implements DatabaseConnection {
    @Override
    public void connect(String host, int port, String username, String password) {
        System.out.println("Connecting to MySQL at " + host + ":" + port);
        System.out.println("Username: " + username);
    }
    
    @Override
    public void executeQuery(String sql) {
        System.out.println("MySQL executing: " + sql);
    }
    
    @Override
    public void disconnect() {
        System.out.println("Disconnecting from MySQL");
    }
}

public class PostgreSQLConnection implements DatabaseConnection {
    @Override
    public void connect(String host, int port, String username, String password) {
        System.out.println("Connecting to PostgreSQL at " + host + ":" + port);
        System.out.println("User: " + username);
    }
    
    @Override
    public void executeQuery(String sql) {
        System.out.println("PostgreSQL executing: " + sql);
    }
    
    @Override
    public void disconnect() {
        System.out.println("Disconnecting from PostgreSQL");
    }
}

// Step 3: สร้าง Factory
public class DatabaseConnectionFactory {
    
    // Factory method - รับเข้า type แล้วสร้าง object ที่เหมาะสม
    public static DatabaseConnection createConnection(String dbType) {
        switch (dbType.toLowerCase()) {
            case "mysql":
                return new MySQLConnection();
            case "postgresql":
                return new PostgreSQLConnection();
            default:
                throw new IllegalArgumentException("Unknown database type: " + dbType);
        }
    }
}

// Step 4: ใช้ Factory
public class DataService {
    
    private DatabaseConnection connection;
    
    // ข้อความแนะนำ: ต้องใช้ factory แล้ว
    public void setupDatabase(String dbType) {
        // ← Factory สร้าง connection ให้
        this.connection = DatabaseConnectionFactory.createConnection(dbType);
        this.connection.connect("localhost", 5432, "admin", "password");
    }
    
    public void performQuery(String sql) {
        connection.executeQuery(sql);
    }
    
    public void closeDatabase() {
        connection.disconnect();
    }
}

// ใช้งาน
public class FactoryPatternDemo {
    public static void main(String[] args) {
        
        // MySQL
        System.out.println("=== MySQL Setup ===");
        DataService mysqlService = new DataService();
        mysqlService.setupDatabase("mysql");
        mysqlService.performQuery("SELECT * FROM users");
        mysqlService.closeDatabase();
        
        System.out.println("\n=== PostgreSQL Setup ===");
        DataService postgresService = new DataService();
        postgresService.setupDatabase("postgresql");
        postgresService.performQuery("SELECT * FROM users");
        postgresService.closeDatabase();
    }
}

// OUTPUT:
// === MySQL Setup ===
// Connecting to MySQL at localhost:5432
// Username: admin
// MySQL executing: SELECT * FROM users
// Disconnecting from MySQL
// 
// === PostgreSQL Setup ===
// Connecting to PostgreSQL at localhost:5432
// User: admin
// PostgreSQL executing: SELECT * FROM users
// Disconnecting from PostgreSQL

ข้อดีของ Factory Pattern:

  • Decoupling – DataService ไม่ต้องรู้เกี่ยวกับ MySQLConnection หรือ PostgreSQLConnection
  • Flexibility – เพิ่ม database type ใหม่เพียงแค่ extend interface และเพิ่มลงใน factory
  • Testability – สามารถ mock DatabaseConnection ได้ง่าย
  • Centralized – ลอจิกในการเลือก type อยู่ที่เดียว

Abstract Factory: Families of Related Objects

บางครั้ง objects ที่สร้างต้องเป็น “family” ที่เกี่ยวข้องกัน ตัวอย่าง UI frameworks ต่างประเทศอาจมี button, text field ต่างกัน:

java// ต้องสร้าง families ของ UI components
public interface UIComponentFactory {
    Button createButton();
    TextField createTextField();
    Dialog createDialog();
}

// Concrete factory สำหรับ Windows UI
public class WindowsUIFactory implements UIComponentFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
    
    @Override
    public TextField createTextField() {
        return new WindowsTextField();
    }
    
    @Override
    public Dialog createDialog() {
        return new WindowsDialog();
    }
}

// Concrete factory สำหรับ Mac UI
public class MacUIFactory implements UIComponentFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }
    
    @Override
    public TextField createTextField() {
        return new MacTextField();
    }
    
    @Override
    public Dialog createDialog() {
        return new MacDialog();
    }
}

// Interfaces สำหรับ components
public interface Button {
    void render();
}

public interface TextField {
    void render();
}

public interface Dialog {
    void show();
}

// Windows implementations
public class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering Windows-style button");
    }
}

public class WindowsTextField implements TextField {
    @Override
    public void render() {
        System.out.println("Rendering Windows-style text field");
    }
}

public class WindowsDialog implements Dialog {
    @Override
    public void show() {
        System.out.println("Showing Windows-style dialog");
    }
}

// Mac implementations
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering Mac-style button");
    }
}

public class MacTextField implements TextField {
    @Override
    public void render() {
        System.out.println("Rendering Mac-style text field");
    }
}

public class MacDialog implements Dialog {
    @Override
    public void show() {
        System.out.println("Showing Mac-style dialog");
    }
}

// Application ที่ใช้ factory
public class UIApplication {
    private UIComponentFactory factory;
    private Button button;
    private TextField textField;
    private Dialog dialog;
    
    public UIApplication(UIComponentFactory factory) {
        this.factory = factory;
    }
    
    public void createUI() {
        // สร้าง family ของ components ที่เหมาะสม
        button = factory.createButton();
        textField = factory.createTextField();
        dialog = factory.createDialog();
    }
    
    public void render() {
        button.render();
        textField.render();
        dialog.show();
    }
}

// ใช้งาน
public class AbstractFactoryDemo {
    public static void main(String[] args) {
        // Windows application
        System.out.println("=== Windows UI ===");
        UIApplication winApp = new UIApplication(new WindowsUIFactory());
        winApp.createUI();
        winApp.render();
        
        // Mac application
        System.out.println("\n=== Mac UI ===");
        UIApplication macApp = new UIApplication(new MacUIFactory());
        macApp.createUI();
        macApp.render();
    }
}

// OUTPUT:
// === Windows UI ===
// Rendering Windows-style button
// Rendering Windows-style text field
// Showing Windows-style dialog
//
// === Mac UI ===
// Rendering Mac-style button
// Rendering Mac-style text field
// Showing Mac-style dialog

Abstract Factory คือ:

  • Factory ที่สร้าง family ของ related objects
  • ใช้เมื่อต้องสร้าง objects ที่ต้องทำงานร่วมกัน
  • ตัวอย่างจริง: Swing, JavaFX UI libraries ใช้ pattern นี้

Structural Patterns: จัดระเบียบโครงสร้าง

ตัวอย่างที่ 2: Adapter Pattern

Adapter Pattern คล้ายกับ adapter ในชีวิตจริง (เช่น adapter ไฟ) มันแปลง interface ของ object หนึ่งเป็น interface ที่ client ต้องการ:

java// สมมติ: เรามี legacy system ที่ใช้ old interface
public interface OldPaymentSystem {
    void makePayment(double amount);
}

public class LegacyPaymentGateway implements OldPaymentSystem {
    @Override
    public void makePayment(double amount) {
        System.out.println("Legacy system: Processing payment of $" + amount);
    }
}

// แต่ application ใหม่คาดหวัง interface ต่างกัน
public interface NewPaymentSystem {
    void processTransaction(double money, String currency);
}

// สร้าง adapter เพื่อ connect legacy system กับ new interface
public class PaymentSystemAdapter implements NewPaymentSystem {
    
    private OldPaymentSystem legacySystem;
    
    public PaymentSystemAdapter(OldPaymentSystem legacySystem) {
        this.legacySystem = legacySystem;
    }
    
    @Override
    public void processTransaction(double money, String currency) {
        // แปลง new interface เป็น old interface
        System.out.println("Adapter: Converting " + money + " " + currency + " to old format");
        
        // เรียก legacy system
        legacySystem.makePayment(money);
    }
}

// ใช้งาน
public class AdapterPatternDemo {
    public static void main(String[] args) {
        // Legacy system
        OldPaymentSystem oldSystem = new LegacyPaymentGateway();
        
        // Adapter ให้ legacy system ใช้งานได้กับ new interface
        NewPaymentSystem adapter = new PaymentSystemAdapter(oldSystem);
        
        // Application ใช้ new interface โดยไม่รู้ว่า underlying system เป็น legacy
        adapter.processTransaction(100.50, "USD");
    }
}

// OUTPUT:
// Adapter: Converting 100.5 USD to old format
// Legacy system: Processing payment of $100.5

Adapter Pattern ใช้เมื่อ:

  • ต้องใช้ class/library เดิม แต่ interface ไม่ตรงกัน
  • ต้อง integrate legacy code กับ new code
  • ต้องให้ incompatible interfaces ทำงานร่วมกัน

ตัวอย่างที่ 3: Decorator Pattern

Decorator Pattern อนุญาตให้ “ตกแต่ง” (decorate) objects ด้วยฟังก์ชันการทำงานใหม่โดยไม่แก้ไข object เดิม:

java// Base interface สำหรับ beverage (เครื่องดื่ม)
public interface Beverage {
    String getDescription();
    double getCost();
}

// Concrete beverage
public class Coffee implements Beverage {
    @Override
    public String getDescription() {
        return "Coffee";
    }
    
    @Override
    public double getCost() {
        return 2.99;
    }
}

public class Tea implements Beverage {
    @Override
    public String getDescription() {
        return "Tea";
    }
    
    @Override
    public double getCost() {
        return 1.99;
    }
}

// Decorator base class
public abstract class BeverageDecorator implements Beverage {
    // ← ให้ reference ไป beverage ที่ decorate
    protected Beverage beverage;
    
    public BeverageDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// Concrete decorators - แต่ละตัว "ตกแต่ง" beverage ด้วยการเพิ่ม feature
public class MilkDecorator extends BeverageDecorator {
    public MilkDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Milk";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 0.50;
    }
}

public class WhippedCreamDecorator extends BeverageDecorator {
    public WhippedCreamDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whipped Cream";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 0.75;
    }
}

public class CinnamonDecorator extends BeverageDecorator {
    public CinnamonDecorator(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Cinnamon";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 0.25;
    }
}

// ใช้งาน - สามารถ "layer" decorators
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        // Coffee with milk
        Beverage coffee1 = new MilkDecorator(new Coffee());
        System.out.println(coffee1.getDescription() + " - $" + coffee1.getCost());
        
        // Coffee with milk and whipped cream
        Beverage coffee2 = new WhippedCreamDecorator(
            new MilkDecorator(new Coffee())
        );
        System.out.println(coffee2.getDescription() + " - $" + coffee2.getCost());
        
        // Coffee with milk, whipped cream, and cinnamon
        Beverage coffee3 = new CinnamonDecorator(
            new WhippedCreamDecorator(
                new MilkDecorator(new Coffee())
            )
        );
        System.out.println(coffee3.getDescription() + " - $" + coffee3.getCost());
        
        // Tea with cinnamon
        Beverage tea = new CinnamonDecorator(new Tea());
        System.out.println(tea.getDescription() + " - $" + tea.getCost());
    }
}

// OUTPUT:
// Coffee, Milk - $3.49
// Coffee, Milk, Whipped Cream - $4.24
// Coffee, Milk, Whipped Cream, Cinnamon - $4.49
// Tea, Cinnamon - $2.24

ข้อดีของ Decorator Pattern:

  • Flexible – สามารถ combine decorators ได้หลายวิธี
  • Open/Closed principle – class ปิดต่อการแก้ไข แต่เปิดต่อการขยาย
  • Avoids subclass explosion – ไม่ต้องสร้าง CoffeeWithMilkAndWhippedCream class
  • Dynamic – สามารถเพิ่ม/ลบ decorators ที่ runtime

Behavioral Patterns: จัดการพฤติกรรม

ตัวอย่างที่ 4: Observer Pattern

Observer Pattern ใช้เมื่อ object หลาย ๆ ตัว (observers) ต้องรู้เรื่องการเปลี่ยนแปลงของ object อีกตัว (subject):

javaimport java.util.*;

// Subject - object ที่เปลี่ยนแปลง
public interface StockPrice {
    void addObserver(StockObserver observer);
    void removeObserver(StockObserver observer);
    void notifyObservers();
}

// Observer - รับการแจ้งเตือนจาก subject
public interface StockObserver {
    void update(String stockSymbol, double price);
}

// Concrete subject
public class Stock implements StockPrice {
    private String symbol;
    private double price;
    private List<StockObserver> observers = new ArrayList<>();
    
    public Stock(String symbol, double initialPrice) {
        this.symbol = symbol;
        this.price = initialPrice;
    }
    
    @Override
    public void addObserver(StockObserver observer) {
        observers.add(observer);
        System.out.println("Observer added. Total observers: " + observers.size());
    }
    
    @Override
    public void removeObserver(StockObserver observer) {
        observers.remove(observer);
        System.out.println("Observer removed. Total observers: " + observers.size());
    }
    
    @Override
    public void notifyObservers() {
        // ← ส่ง notification ไป observers ทั้งหมด
        for (StockObserver observer : observers) {
            observer.update(symbol, price);
        }
    }
    
    public void setPrice(double newPrice) {
        this.price = newPrice;
        System.out.println("\nStock price changed: " + symbol + " = $" + price);
        
        // เมื่อราคาเปลี่ยน ให้แจ้ง observers
        notifyObservers();
    }
    
    public double getPrice() {
        return price;
    }
}

// Concrete observers
public class Investor implements StockObserver {
    private String name;
    
    public Investor(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String stockSymbol, double price) {
        System.out.println("Investor " + name + " notified: " + stockSymbol + 
                         " is now $" + price);
    }
}

public class TradingBot implements StockObserver {
    @Override
    public void update(String stockSymbol, double price) {
        if (price > 150) {
            System.out.println("Bot: SELL signal for " + stockSymbol + 
                             " (too high at $" + price + ")");
        } else if (price < 100) {
            System.out.println("Bot: BUY signal for " + stockSymbol + 
                             " (too low at $" + price + ")");
        }
    }
}

public class NewsAgency implements StockObserver {
    @Override
    public void update(String stockSymbol, double price) {
        System.out.println("News: Breaking - " + stockSymbol + 
                         " stock price is now $" + price);
    }
}

// ใช้งาน
public class ObserverPatternDemo {
    public static void main(String[] args) {
        // สร้าง stock
        Stock apple = new Stock("AAPL", 120);
        
        // สร้าง observers
        Investor investor1 = new Investor("Alice");
        Investor investor2 = new Investor("Bob");
        TradingBot bot = new TradingBot();
        NewsAgency news = new NewsAgency();
        
        // ลงทะเบียน observers
        System.out.println("=== Registering observers ===");
        apple.addObserver(investor1);
        apple.addObserver(investor2);
        apple.addObserver(bot);
        apple.addObserver(news);
        
        // ราคาเปลี่ยน - observers ได้รับการแจ้งเตือน
        System.out.println("\n=== Price changes ===");
        apple.setPrice(130);
        apple.setPrice(95);
        apple.setPrice(155);
        
        // ยกเลิกลงทะเบียน
        System.out.println("\n=== Unregistering investor2 ===");
        apple.removeObserver(investor2);
        
        apple.setPrice(140);
    }
}

// OUTPUT:
// === Registering observers ===
// Observer added. Total observers: 1
// Observer added. Total observers: 2
// Observer added. Total observers: 3
// Observer added. Total observers: 4
//
// === Price changes ===
// 
// Stock price changed: AAPL = $130.0
// Investor Alice notified: AAPL is now $130.0
// Investor Bob notified: AAPL is now $130.0
// Bot: (no action - price ok)
// News: Breaking - AAPL stock price is now $130.0
// ...

Observer Pattern ใช้เมื่อ:

  • Object หนึ่ง ต้องแจ้งข้อมูล ให้ objects อื่น ๆ หลาย ตัว
  • ต้องลดการพึ่งพา (loose coupling) ระหว่างแจ้ง (subject) และ ผู้รับข่าว (observers)
  • ตัวอย่างจริง: event listeners, pub-sub systems, MVC updates

Best Practices & Cautions

เมื่อเลือก Design Patterns:

ข้อที่ต้องจำ:

  1. ไม่ใช้ patterns เพื่อใช้ patterns – patterns คือ tool สำหรับแก้ปัญหา ไม่ใช่ปลายทาง
  2. เข้าใจปัญหาก่อน – รู้ว่าคุณแก้ปัญหาอะไร ก่อนเลือก pattern
  3. เรียบง่าย – choose simple design ก่อน เพิ่มความซับซ้อนเมื่อจำเป็น (YAGNI – You Aren’t Gonna Need It)

Signs ที่ต้องใช้ Design Patterns:

  • Code มี duplication – ตัด โดยใช้ pattern เหมาะสม
  • Tight coupling – ลดการพึ่งพา โดยใช้ pattern
  • Hard to extend – ทำให้ flexible โดยใช้ pattern
  • Multiple responsibilities – แยกความรับผิดชอบ โดยใช้ pattern

Common Mistakes:

  • Over-engineering – ใช้ complex patterns สำหรับ simple problem
  • Pattern abuse – force pattern ไปใช้ที่ไม่เหมาะ
  • Ignoring context – apply pattern เหมือนกันในทุกสถานการณ์
  • Over-abstraction – abstraction เกินไป ทำให้ code ยุ่ง

Main Design Patterns Categories

Creational (5 patterns):

  • Singleton – แน่ใจว่ามี instance เดียว
  • Factory Method – สร้าง objects โดยไม่รู้ concrete class
  • Abstract Factory – สร้าง families ของ related objects
  • Builder – สร้าง complex objects step-by-step
  • Prototype – clone existing objects

Structural (7 patterns):

  • Adapter – ทำให้ incompatible interfaces ทำงานร่วมกัน
  • Bridge – decouple abstraction จาก implementation
  • Composite – compose objects เป็น tree structures
  • Decorator – dynamically add behaviors ไป objects
  • Facade – provide simplified interface ไปยัง complex subsystems
  • Flyweight – share common state ระหว่าง objects
  • Proxy – provide surrogate ของ object

Behavioral (11 patterns):

  • Chain of Responsibility – pass request ไปตามchain
  • Command – encapsulate request เป็น object
  • Interpreter – define grammar และ interpret
  • Iterator – access elements ของ collection sequentially
  • Mediator – reduce coupling โดยใช้ mediator object
  • Memento – capture internal state โดยไม่ violate encapsulation
  • Observer – notify multiple objects เกี่ยวกับ state changes
  • State – allow object เปลี่ยนแปลงพฤติกรรมเมื่อ state เปลี่ยน
  • Strategy – define family ของ algorithms
  • Template Method – define skeleton ของ algorithm ใน base class
  • Visitor – add new operations ไปยัง objects โดยไม่แก้ไข

ตัวอย่างที่ 5: Strategy Pattern

Encapsulate Algorithms

Strategy Pattern ใช้เมื่อมี algorithms หลายตัว ที่สามารถ interchangeable:

java// Strategy interface - define family ของ sorting algorithms
public interface SortingStrategy {
    void sort(int[] array);
}

// Concrete strategies
public class QuickSort implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Sorting using QuickSort algorithm");
        // QuickSort implementation
        quickSort(array, 0, array.length - 1);
    }
    
    private void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pi = partition(arr, low, high);
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    
    private int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;
        return i + 1;
    }
}

public class MergeSort implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Sorting using MergeSort algorithm");
        mergeSort(array, 0, array.length - 1);
    }
    
    private void mergeSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = left + (right - left) / 2;
            mergeSort(arr, left, mid);
            mergeSort(arr, mid + 1, right);
            merge(arr, left, mid, right);
        }
    }
    
    private void merge(int[] arr, int left, int mid, int right) {
        // Merge logic
    }
}

public class BubbleSort implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Sorting using BubbleSort algorithm");
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

// Context - ใช้ strategy
public class DataProcessor {
    private SortingStrategy strategy;
    
    // Set strategy ที่ runtime
    public void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void processData(int[] data) {
        if (strategy == null) {
            throw new IllegalStateException("Strategy not set");
        }
        
        System.out.println("Processing data with selected strategy...");
        strategy.sort(data);
    }
}

// ใช้งาน
public class StrategyPatternDemo {
    public static void main(String[] args) {
        DataProcessor processor = new DataProcessor();
        int[] data = {64, 34, 25, 12, 22, 11, 90};
        
        // เปลี่ยน strategy ที่ runtime
        System.out.println("=== Using QuickSort ===");
        processor.setStrategy(new QuickSort());
        processor.processData(data);
        
        System.out.println("\n=== Using MergeSort ===");
        processor.setStrategy(new MergeSort());
        processor.processData(data);
        
        System.out.println("\n=== Using BubbleSort ===");
        processor.setStrategy(new BubbleSort());
        processor.processData(data);
    }
}

// OUTPUT:
// === Using QuickSort ===
// Processing data with selected strategy...
// Sorting using QuickSort algorithm
// 
// === Using MergeSort ===
// Processing data with selected strategy...
// Sorting using MergeSort algorithm
// 
// === Using BubbleSort ===
// Processing data with selected strategy...
// Sorting using BubbleSort algorithm

Strategy Pattern ข้อดี:

  • Runtime flexibility – เปลี่ยน algorithm ที่ runtime
  • Encapsulation – แต่ละ algorithm ใน class ของตัวเอง
  • Easy to add new strategies – add strategy ใหม่ไม่ต้องแก้ existing code
  • Avoids conditional logic – แทนที่ if-else chains

สรุป

Advanced Design Patterns ให้วิธี systematic ในการแก้ปัญหา common ของ software design:

Creational Patterns:

  • Factory – สร้าง objects อย่างยืดหยุ่น
  • Abstract Factory – สร้าง families ของ related objects
  • Builder – สร้าง complex objects step-by-step
  • Singleton – ใช้มี instance เดียว

Structural Patterns:

  • Adapter – ให้ incompatible classes ทำงานร่วมกัน
  • Decorator – เพิ่ม behaviors dynamically
  • Facade – provide simplified interface
  • Proxy – control access ไปยัง objects

Behavioral Patterns:

  • Observer – notify multiple objects เกี่ยวกับ state changes
  • Strategy – encapsulate interchangeable algorithms
  • State – เปลี่ยน behavior ตามเปลี่ยน state
  • Command – encapsulate requests เป็น objects

แนวทางการใช้:

  • เข้าใจปัญหาก่อน เลือก pattern หลัง
  • ไม่ใช้ over-engineer
  • เลือก simplest solution ที่ solves problem
  • เรียนรู้ trade-offs ของแต่ละ pattern

Design patterns ไม่ใช่silver bullet แต่เป็น toolbox ของวิธีแก้ปัญหา proven ที่คุณสามารถใช้เมื่อสถานการณ์เหมาะสม