Skip to main content

เนื้อหาภาษาไทย / Thai content

System guide Content reviewed

การใช้ Design Patterns ใน PHP

บทความนี้แนะนำ Design Patterns ใน PHP เพื่อสร้างโค้ดที่มีโครงสร้างดี, อ่านง่าย, และบำรุงรักษาง่าย โดยอธิบายถึง Singleton, Factory, และ Observer Patterns พร้อมตั...

การใช้ Design Patterns ใน PHP

ในบทก่อนหน้านี้ เราได้เรียนรู้วิธีการสร้าง Command-Line Interface (CLI) Application ด้วย PHP ซึ่งช่วยให้เราสามารถสร้างเครื่องมือที่ทำงานได้หลากหลายจาก command line ในบทนี้ เราจะมาเรียนรู้เกี่ยวกับ Design Patterns ซึ่งเป็นแนวทางการออกแบบที่ช่วยให้เราเขียนโค้ดที่มีโครงสร้างที่ดี, อ่านง่าย, และง่ายต่อการบำรุงรักษา โดยเฉพาะอย่างยิ่งเมื่อโปรเจคของเรามีขนาดใหญ่และซับซ้อนขึ้น

Design Patterns คืออะไร?

Design Patterns คือโซลูชันที่ได้รับการพิสูจน์แล้วสำหรับปัญหาการออกแบบซ้ำๆ ที่มักเกิดขึ้นในการพัฒนาซอฟต์แวร์ ไม่ใช่โค้ดสำเร็จรูปที่เราสามารถคัดลอกและวางได้ทันที แต่เป็นพิมพ์เขียว (blueprint) หรือแนวคิดที่เราสามารถนำไปปรับใช้ให้เข้ากับบริบทของโปรเจคของเราได้

Benefits of using Design Patterns:

  • Reusability: Design Patterns ช่วยให้เรานำแนวคิดการออกแบบกลับมาใช้ใหม่ได้ในหลายๆ โปรเจค
  • Readability: โค้ดที่ใช้ Design Patterns มักจะอ่านง่ายและเข้าใจได้ง่ายกว่า เพราะมีโครงสร้างที่เป็นมาตรฐาน
  • Maintainability: การแก้ไขและบำรุงรักษาโค้ดที่ใช้ Design Patterns ทำได้ง่ายกว่า เพราะโค้ดมีโครงสร้างที่ชัดเจนและแยกส่วนกัน
  • Scalability: Design Patterns ช่วยให้เราสร้างแอปพลิเคชันที่สามารถขยายขนาดได้ง่ายขึ้น

ในบทนี้ เราจะมาดู Design Patterns ที่พบบ่อยและมีประโยชน์ 3 แบบ คือ Singleton, Factory, และ Observer พร้อมตัวอย่างการใช้งานใน PHP

Singleton Pattern

Singleton Pattern คือ Design Pattern ที่รับประกันว่าจะมีเพียงอินสแตนซ์เดียวของคลาสหนึ่งๆ ในระบบ และมีจุดเข้าถึงส่วนกลาง (global access point) เพื่อให้เราสามารถเข้าถึงอินสแตนซ์นั้นได้จากทุกที่ในโค้ด

เหตุผลในการใช้งาน: Singleton เหมาะสำหรับสถานการณ์ที่เราต้องการควบคุมการสร้างอินสแตนซ์ของคลาสอย่างเข้มงวด เช่น การจัดการการเชื่อมต่อฐานข้อมูล, การจัดการการตั้งค่าของระบบ, หรือการจัดการ Log

ตัวอย่างใน PHP:

class Database {
    private static $instance;
    private $connection;

    private function __construct() {
        // ป้องกันการสร้างอินสแตนซ์จากภายนอก
        $this->connection = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'password');
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new Database();
        }

        return self::$instance;
    }

    public function getConnection() {
        return $this->connection;
    }

    private function __clone() {
        // ป้องกันการ clone
    }

    public function __wakeup() {
        // ป้องกันการ unserialize
        throw new \Exception("Cannot unserialize singleton");
    }
}

// ใช้งาน
$db = Database::getInstance();
$connection = $db->getConnection();

// การพยายามสร้างอินสแตนซ์ใหม่โดยตรงจะทำให้เกิดข้อผิดพลาด
// $db2 = new Database(); // Error: Call to private Database::__construct()

คำอธิบาย:

  • เราสร้าง constructor เป็น `private` เพื่อป้องกันการสร้างอินสแตนซ์จากภายนอก
  • เราสร้าง method `getInstance()` ที่เป็น `static` เพื่อให้สามารถเข้าถึงได้โดยไม่ต้องสร้างอินสแตนซ์ของคลาส
  • ภายใน `getInstance()` เราตรวจสอบว่ามีอินสแตนซ์อยู่แล้วหรือไม่ ถ้ายังไม่มี ก็สร้างอินสแตนซ์ใหม่ แล้วเก็บไว้ใน `$instance`
  • เราสร้าง `__clone()` และ `__wakeup()` เป็น `private` เพื่อป้องกันการ clone และ unserialize อินสแตนซ์

Trade-offs: Singleton อาจทำให้การทดสอบยากขึ้น เพราะเป็นการสร้าง dependency ที่ซ่อนอยู่ และอาจทำให้โค้ดมีความยืดหยุ่นน้อยลง

Factory Pattern

Factory Pattern คือ Design Pattern ที่ใช้ในการสร้างอ็อบเจ็กต์โดยไม่ต้องระบุคลาสที่แน่นอนที่จะสร้าง ณ จุดที่เรียกใช้งาน โดยจะมอบหมายความรับผิดชอบในการสร้างอ็อบเจ็กต์ให้กับคลาสอื่นที่เรียกว่า Factory

เหตุผลในการใช้งาน: Factory เหมาะสำหรับสถานการณ์ที่เราต้องการสร้างอ็อบเจ็กต์ที่มีความซับซ้อน หรือต้องการเปลี่ยนคลาสที่ใช้สร้างอ็อบเจ็กต์ได้ง่าย โดยไม่ต้องแก้ไขโค้ดที่เรียกใช้งาน

ตัวอย่างใน PHP:

interface Vehicle {
    public function drive();
}

class Car implements Vehicle {
    public function drive() {
        return "Driving a car";
    }
}

class Truck implements Vehicle {
    public function drive() {
        return "Driving a truck";
    }
}

class VehicleFactory {
    public static function createVehicle(string $type): Vehicle {
        switch ($type) {
            case 'car':
                return new Car();
            case 'truck':
                return new Truck();
            default:
                throw new \Exception("Invalid vehicle type");
        }
    }
}

// ใช้งาน
$car = VehicleFactory::createVehicle('car');
echo $car->drive(); // Output: Driving a car

$truck = VehicleFactory::createVehicle('truck');
echo $truck->drive(); // Output: Driving a truck

คำอธิบาย:

  • เราสร้าง interface `Vehicle` เพื่อกำหนดสัญญา (contract) สำหรับคลาสรถทุกประเภท
  • เราสร้างคลาส `Car` และ `Truck` ที่ implements interface `Vehicle`
  • เราสร้างคลาส `VehicleFactory` ที่มี method `createVehicle()` ซึ่งรับ type ของรถเป็น parameter และสร้างอินสแตนซ์ของคลาสรถที่ถูกต้อง

Trade-offs: Factory Pattern อาจเพิ่มความซับซ้อนให้กับโค้ดเล็กน้อย แต่ช่วยให้โค้ดมีความยืดหยุ่นและง่ายต่อการขยายในระยะยาว

Observer Pattern

Observer Pattern คือ Design Pattern ที่กำหนดความสัมพันธ์แบบ one-to-many ระหว่างอ็อบเจ็กต์ โดยเมื่อ state ของอ็อบเจ็กต์หนึ่ง (Subject) เปลี่ยนแปลง, อ็อบเจ็กต์อื่นๆ (Observers) ที่ขึ้นอยู่กับอ็อบเจ็กต์นั้นจะได้รับการแจ้งเตือนและอัปเดตโดยอัตโนมัติ

เหตุผลในการใช้งาน: Observer เหมาะสำหรับสถานการณ์ที่เราต้องการให้หลายๆ อ็อบเจ็กต์ตอบสนองต่อการเปลี่ยนแปลงของ state ของอ็อบเจ็กต์อื่น โดยไม่ต้องให้ Subject รู้จัก Observer โดยตรง

ตัวอย่างใน PHP:

interface Observer {
    public function update(Subject $subject);
}

interface Subject {
    public function attach(Observer $observer);
    public function detach(Observer $observer);
    public function notify();
}

class User implements Subject {
    private $observers = [];
    private $username;

    public function __construct(string $username) {
        $this->username = $username;
    }

    public function attach(Observer $observer) {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer) {
        $this->observers = array_filter($this->observers, function($o) use ($observer) {
            return ($o !== $observer);
        });
    }

    public function notify() {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function changeUsername(string $newUsername) {
        $this->username = $newUsername;
        $this->notify();
    }

    public function getUsername() {
        return $this->username;
    }
}

class EmailNotification implements Observer {
    public function update(Subject $subject) {
        echo "Sending email notification to " . $subject->getUsername() . "\n";
    }
}

class LogNotification implements Observer {
    public function update(Subject $subject) {
        echo "Logging username change for " . $subject->getUsername() . "\n";
    }
}

// ใช้งาน
$user = new User("john.doe");

$emailNotification = new EmailNotification();
$logNotification = new LogNotification();

$user->attach($emailNotification);
$user->attach($logNotification);

$user->changeUsername("jane.doe");
// Output:
// Sending email notification to jane.doe
// Logging username change for jane.doe

คำอธิบาย:

  • เราสร้าง interface `Observer` เพื่อกำหนด contract สำหรับ observers
  • เราสร้าง interface `Subject` เพื่อกำหนด contract สำหรับ subjects
  • คลาส `User` implements `Subject` และมี method สำหรับ attach, detach, และ notify observers
  • คลาส `EmailNotification` และ `LogNotification` implements `Observer` และมี logic สำหรับการตอบสนองต่อการเปลี่ยนแปลงของ subject

Trade-offs: Observer Pattern อาจทำให้เกิดการ coupling ที่หลวมๆ ระหว่าง Subject และ Observers ซึ่งอาจทำให้ยากต่อการติดตามการเปลี่ยนแปลงของ state

สรุป

Design Patterns เป็นเครื่องมือที่มีประโยชน์ในการพัฒนาซอฟต์แวร์ที่ซับซ้อน ช่วยให้เราเขียนโค้ดที่มีโครงสร้างที่ดี, อ่านง่าย, และง่ายต่อการบำรุงรักษา ในบทนี้ เราได้เรียนรู้ Design Patterns ที่พบบ่อย 3 แบบ คือ Singleton, Factory, และ Observer พร้อมตัวอย่างการใช้งานใน PHP การทำความเข้าใจ Design Patterns และการนำไปประยุกต์ใช้ในการพัฒนาโปรเจคของเรา จะช่วยให้เราเป็นนักพัฒนาซอฟต์แวร์ที่มีประสิทธิภาพมากยิ่งขึ้น

ในบทต่อไป เราจะมาเรียนรู้เกี่ยวกับการปรับปรุงประสิทธิภาพ PHP Application เพื่อให้แอปพลิเคชันของเราทำงานได้รวดเร็วและมีประสิทธิภาพมากยิ่งขึ้น

Office.in.th Custom Service

Want to build or adapt a system from this structure?

Office.in.th designs and develops enterprise web applications, API integrations, and public- and private-sector management systems, with long-term support.

See our software development service