ในฐานะนักพัฒนา PHP ระดับ Senior การทำความเข้าใจ Design Patterns ไม่ใช่แค่การท่องจำโค้ด แต่คือการเรียนรู้ ‘วิธีแก้ปัญหา’ ที่ผ่านการพิสูจน์แล้ว (Proven Solutions) มันเปรียบเสมือนชุดเครื่องมือทางความคิดที่ช่วยให้เราเขียนโค้ดที่มีโครงสร้างชัดเจน, ยืดหยุ่นสูง (Flexible), และง่ายต่อการบำรุงรักษา (Maintainable) โดยเฉพาะอย่างยิ่งเมื่อระบบขยายตัวและมีนักพัฒนาหลายคนเข้ามาเกี่ยวข้อง
- Design Pattern คืออะไร? มันคือพิมพ์เขียว (Blueprint) ของการแก้ปัญหาทางซอฟต์แวร์ที่เกิดขึ้นบ่อยครั้ง ไม่ใช่โค้ดสำเร็จรูป แต่เป็นแนวคิดเชิงสถาปัตยกรรม
- ทำไมต้องใช้ Patterns ใน PHP? เพื่อให้เราหลีกเลี่ยงการเขียนโค้ดแบบ “God Object” (คลาสที่รับผิดชอบทุกอย่าง) และส่งเสริมหลักการ SOLID โดยเฉพาะ Single Responsibility Principle ทำให้เมื่อคุณต้องการเพิ่มฟีเจอร์ใหม่ คุณไม่ต้องไปแตะโค้ดส่วนอื่น ๆ ที่ทำงานอยู่แล้ว
2. ตัวอย่างการใช้งานและรูปแบบโค้ด (Code Examples)
เราจะมาดูตัวอย่าง 3 รูปแบบหลักที่ใช้บ่อยที่สุดใน PHP โดยใช้สถานการณ์จำลองคือ “ระบบประมวลผลคำสั่งซื้อสินค้า” เพื่อให้เห็นภาพการทำงานร่วมกันของ Patterns เหล่านี้
- Strategy Pattern (กลยุทธ์): ใช้เมื่อคุณมีอัลกอริทึมหลายแบบที่สามารถสลับใช้ได้ตามบริบท เช่น วิธีการคำนวณส่วนลด หรือวิธีการชำระเงินต่าง ๆ
- Factory Pattern (โรงงาน): ใช้เพื่อแยกความรับผิดชอบในการสร้าง Object ออกจากโค้ดหลัก ทำให้เราไม่จำเป็นต้องรู้ว่าคลาสที่แท้จริงคืออะไร เพียงแค่บอก Factory ว่าต้องการ “Product Type” อะไร
- Singleton Pattern (เอกเทศ): ใช้เมื่อคุณมั่นใจว่าในระบบนั้นจะต้องมี Instance ของ Class นั้น ๆ ได้เพียงตัวเดียวเท่านั้น เช่น การจัดการ Log File หรือการเชื่อมต่อฐานข้อมูลหลัก
<?php declare(strict_types=1);
// ========================================
// 1. STRATEGY PATTERN (การชำระเงิน)
// ========================================
/**
* Interface กำหนดมาตรฐานของ Payment Strategy ทั้งหมด
*/
interface PaymentStrategy {
public function pay(float $amount): bool;
}
/**
* Concrete Strategy: การจ่ายด้วยบัตรเครดิต
*/
class CreditCardPayment implements PaymentStrategy {
private string $cardNumber;
public function __construct(string $card) {
$this->cardNumber = $card;
}
public function pay(float $amount): bool {
echo "💳 Processing payment of $" . number_format($amount, 2) . " using Credit Card ending in " . substr($this->cardNumber, -4) . ". Success!\n";
return true;
}
}
/**
* Concrete Strategy: การจ่ายด้วย PayPal
*/
class PayPalPayment implements PaymentStrategy {
private string $email;
public function __construct(string $email) {
$this->email = $email;
}
public function pay(float $amount): bool {
echo "📧 Processing payment of $" . number_format($amount, 2) . " using PayPal account: {$this->email}. Success!\n";
return true;
}
}
/**
* Context Class: ผู้ประมวลผลคำสั่งซื้อ (ใช้ Strategy)
*/
class ShoppingCart {
private PaymentStrategy $paymentMethod;
// Dependency Injection: รับ PaymentStrategy เข้ามาใน Constructor
public function setPaymentMethod(PaymentStrategy $method): void {
$this->paymentMethod = $method;
}
public function checkout(float $totalAmount): bool {
echo "\n--- Starting Checkout Process ---\n";
return $this->paymentMethod->pay($totalAmount);
}
}
// ========================================
// 2. FACTORY PATTERN (การสร้าง Connection)
// ========================================
/**
* Product Interface: กำหนดมาตรฐานของ Database Connection
*/
interface DatabaseConnection {
public function connect(): void;
}
/**
* Concrete Product A: MySQL Connection
*/
class MySqlConnection implements DatabaseConnection {
private string $host;
public function __construct(string $host) { $this->host = $host; }
public function connect(): void {
echo "✅ Connected to MySQL database at host: {$this->host}.\n";
}
}
/**
* Concrete Product B: PostgreSQL Connection
*/
class PgSqlConnection implements DatabaseConnection {
private string $host;
public function __construct(string $host) { $this->host = $host; }
public function connect(): void {
echo "✅ Connected to PostgreSQL database at host: {$this->host}.\n";
}
}
/**
* Factory Class: รับผิดชอบในการสร้าง Object ที่ซับซ้อน
*/
class DatabaseFactory {
// เมธอดหลักที่ใช้ตัดสินใจว่าจะสร้างคลาสไหน
public static function createConnection(string $type, string $host): ?DatabaseConnection {
switch (strtolower($type)) {
case 'mysql':
return new MySqlConnection($host);
case 'pgsql':
return new PgSqlConnection($host);
default:
echo "❌ Error: Unsupported database type.\n";
return null;
}
}
}
// ========================================
// 3. SINGLETON PATTERN (Logger)
// ========================================
/**
* Singleton Class: รับประกันว่ามี Instance ได้เพียงตัวเดียว
*/
class Logger {
private static ?Logger $instance = null;
private array $logs = [];
// ป้องกันการสร้าง Object จากภายนอกโดยตรง
private function __construct() {}
// เมธอดหลักในการเข้าถึง Instance เดียวกัน
public static function getInstance(): Logger {
if (self::$instance === null) {
self::$instance = new Logger();
}
return self::$instance;
}
// ป้องกันการ Clone Object
private function __clone() {}
// เมธอดสำหรับบันทึก Log
public function log(string $message): void {
$this->logs[] = date('Y-m-d H:i:s') . " - " . $message;
echo "[LOG] Successfully recorded message.\n";
}
// เมธอดสำหรับดู Log ทั้งหมด (เพื่อการสาธิต)
public function getLogs(): array {
return $this->logs;
}
}
// ========================================
// EXECUTION FLOW (การเรียกใช้งานจริง)
// ========================================
echo "========================================\n";
echo " DEMO 1: STRATEGY PATTERN\n";
echo "========================================\n";
$cart = new ShoppingCart();
// Case 1: ใช้ Credit Card Strategy
$ccPayment = new CreditCardPayment("4242-xxxx-xxxx-1234");
$cart->setPaymentMethod($ccPayment);
$cart->checkout(500.00);
echo "\n========================================\n";
echo " DEMO 2: FACTORY PATTERN\n";
echo "========================================\n";
// ใช้ Factory ในการสร้าง Connection โดยไม่ต้องรู้รายละเอียดคลาส
$mysqlConnection = DatabaseFactory::createConnection('MySQL', 'localhost');
if ($mysqlConnection) {
$mysqlConnection->connect();
}
$pgSqlConnection = DatabaseFactory::createConnection('PgSQL', 'db.example.com');
if ($pgSqlConnection) {
$pgSqlConnection->connect();
}
echo "\n========================================\n";
echo " DEMO 3: SINGLETON PATTERN\n";
echo "========================================\n";
// การเข้าถึง Logger Instance ครั้งแรก
$logger1 = Logger::getInstance();
$logger1->log("User logged in successfully.");
// การเข้าถึง Logger Instance ครั้งที่สอง (จะได้ Object เดียวกัน)
$logger2 = Logger::getInstance();
if ($logger1 === $logger2) {
echo "✅ Success: Both variables point to the same single instance of Logger.\n";
}
// ตรวจสอบ Log ที่บันทึกไว้
print_r($logger2->getLogs());
?>
ข้อควรระวัง Security และ Best Practices
- คำนึงถึงความปลอดภัย (Security): เมื่อใช้ Factory หรือ Strategy ควรมีการตรวจสอบ Input Type เสมอ เพื่อป้องกันการถูกโจมตีด้วยค่าที่ไม่คาดคิด (Type Juggling) และควรหลีกเลี่ยงการสร้าง Object จาก Input ของผู้ใช้งานโดยตรง
- Performance & Singleton Critique: Singleton Pattern แม้จะดูง่าย แต่หากใช้ไม่ถูกที่อาจทำให้เกิด Bottleneck ได้ ควรพิจารณาใช้ Lazy Loading หรือ Cache Layer (เช่น Redis/Memcached) ก่อนที่จะตัดสินใจใช้ Singleton กับทรัพยากรที่มีการเข้าถึงสูงมาก ๆ
- Error Handling: ในทุก Pattern ที่มีการสร้าง Object (Factory) ต้องมีการจัดการ Exception อย่างรอบคอบ หากการเชื่อมต่อล้มเหลว ควรโยน `ConnectionException` แทนที่จะปล่อยให้โค้ดทำงานต่อไปด้วย Object ที่ไม่สมบูรณ์
สรุปและการนำไปประยุกต์ใช้งาน
การเลือกใช้ Design Pattern ที่เหมาะสมคือหัวใจสำคัญของการเป็น Senior Developer สิ่งที่ต้องจำไว้เสมอคือ “อย่าใช้ Pattern เพียงเพราะมันดูเท่” (Don’t use a pattern just because it exists) การออกแบบที่ดีต้องตอบโจทย์ปัญหาทางธุรกิจอย่างแท้จริง
- เมื่อไหร่ควรใช้ Strategy? เมื่อคุณมีพฤติกรรมที่แตกต่างกันหลายแบบ (Behavior) และต้องการให้โค้ดหลัก (Context) ไม่ต้องรู้รายละเอียดของแต่ละพฤติกรรม ทำให้ระบบมีความยืดหยุ่นในการเพิ่มวิธีการใหม่ ๆ
- เมื่อไหร่ควรใช้ Factory? เมื่อการสร้าง Object นั้นมีความซับซ้อน มีเงื่อนไขในการเลือกคลาส หรือมีการเปลี่ยนแปลงโครงสร้างของระบบบ่อยครั้ง (เช่น การเปลี่ยนจาก MySQL เป็น NoSQL)
- เมื่อไหร่ควรใช้ Singleton? เมื่อคุณมั่นใจ 100% ว่าทรัพยากรนั้นต้องมี Instance เพียงตัวเดียวตลอดอายุการทำงานของแอปพลิเคชัน (เช่น Configuration Manager, Logger) และไม่มีความจำเป็นที่มันจะต้องถูกแยกส่วน
โดยสรุปแล้ว การออกแบบที่ดีคือการทำให้โค้ดของคุณมีความยืดหยุ่นสูงที่สุด (Highly Decoupled) เพื่อให้ระบบสามารถรองรับการเปลี่ยนแปลงในอนาคตได้อย่างง่ายดายและปลอดภัย ทำให้คุณพร้อมสำหรับการเป็น Senior Developer ที่แท้จริง
อ่านเพิ่มเติม