บทนำ: Design Patterns คืออะไร และทำไมสำคัญ
ในการพัฒนาซอฟต์แวร์ นักพัฒนามักเจอปัญหาเดียวกันซ้ำ ๆ และพบว่าวิธีแก้ปัญหาที่ดี สามารถนำมาใช้ใหม่ได้ในหลาย ๆ โปรเจกต์ Design Pattern คือการรวมรวมประสบการณ์เหล่านั้น เป็นแบบแผนที่พยายามแก้ปัญหาทั่วไป ที่ผ่านการทดสอบและปรับปรุงมาแล้ว
ตัวอย่างของปัญหาที่ design patterns แก้:
- ต้องสร้าง object แต่ไม่รู้ class ไหนจะใช้ (Creational patterns)
- ต้องลดการพึ่งพา (coupling) ระหว่าง classes (Structural patterns)
- ต้องเรียกใช้ method ของ objects ที่แตกต่างกัน (Behavioral patterns)
เหตุผลที่ design patterns สำคัญ:
- Communication – นักพัฒนาคุยกันแล้วบอก “ใช้ Singleton” หรือ “ใช้ Factory” ทุกคนเข้าใจ
- Reusability – solution ที่ proven ใช้ได้ผ่านการทดสอบหลาย projects
- Maintainability – code ที่ follow patterns ง่ายต่อการบำรุงรักษา
- 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:
ข้อที่ต้องจำ:
- ไม่ใช้ patterns เพื่อใช้ patterns – patterns คือ tool สำหรับแก้ปัญหา ไม่ใช่ปลายทาง
- เข้าใจปัญหาก่อน – รู้ว่าคุณแก้ปัญหาอะไร ก่อนเลือก pattern
- เรียบง่าย – 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 ที่คุณสามารถใช้เมื่อสถานการณ์เหมาะสม
