跳到主要内容

เนื้อหาภาษาไทย / 泰语内容

系统指南 内容已审核

Design Patterns: แนวทางการแก้ปัญหาที่พบบ่อย

บทความนี้แนะนำ Design Patterns ซึ่งเป็นแนวทางการแก้ปัญหาการออกแบบซอฟต์แวร์ที่พบบ่อยและได้รับการพิสูจน์แล้วว่ามีประสิทธิภาพ โดย Design Patterns ไม่ใช่โค้ดสำเร็จร...

Design Patterns: แนวทางการแก้ปัญหาที่พบบ่อย

หลังจากที่เราได้เรียนรู้หลักการออกแบบ OOP ที่ดีอย่าง SOLID Principles โดยเฉพาะ Dependency Inversion Principle (DIP) แล้ว เราจะพบว่ามีปัญหาบางอย่างที่เกิดขึ้นซ้ำๆ ในการพัฒนาซอฟต์แวร์ ซึ่งปัญหาเหล่านี้มักจะมีแนวทางการแก้ไขที่เป็นมาตรฐานและได้รับการพิสูจน์แล้วว่ามีประสิทธิภาพ นั่นคือ Design Patterns

Design Patterns คืออะไร?

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

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

เหตุผลที่ต้องใช้ Design Patterns:

  • Code Reusability: ช่วยให้เรานำแนวทางการแก้ปัญหาที่ได้รับการพิสูจน์แล้วกลับมาใช้ใหม่ได้ ลดการคิดค้นวิธีแก้ปัญหาเองตั้งแต่ต้น
  • Improved Communication: สร้างภาษาเดียวกันระหว่างนักพัฒนา ทำให้สื่อสารและเข้าใจโค้ดได้ง่ายขึ้น
  • Better Architecture: ช่วยให้เราออกแบบซอฟต์แวร์ที่มีความยืดหยุ่น บำรุงรักษาง่าย และขยายได้ในอนาคต
  • Proven Solutions: เป็นแนวทางการแก้ปัญหาที่ได้รับการทดสอบและใช้งานจริงมาแล้ว มั่นใจได้ในระดับหนึ่งว่ามีประสิทธิภาพ

ประเภทของ Design Patterns

Design Patterns สามารถแบ่งออกเป็น 3 ประเภทหลักๆ ตามจุดประสงค์ในการใช้งาน:

  1. Creational Patterns: เกี่ยวกับการสร้าง Object อย่างมีประสิทธิภาพ
  2. Structural Patterns: เกี่ยวกับการจัดโครงสร้าง Class และ Object เพื่อสร้างระบบที่ใหญ่และซับซ้อน
  3. Behavioral Patterns: เกี่ยวกับการกำหนดปฏิสัมพันธ์ระหว่าง Object และการจัดการ Algorithm

แต่ละประเภทก็จะมี Design Patterns ย่อยๆ อีกมากมาย แต่ในบทเรียนนี้เราจะมาดูตัวอย่าง Design Patterns ที่สำคัญและใช้งานบ่อยในแต่ละประเภท เพื่อให้เห็นภาพรวมและเข้าใจแนวคิดพื้นฐาน

ตัวอย่าง Design Patterns

ต่อไปนี้เป็นตัวอย่าง Design Patterns ที่พบได้บ่อย พร้อมคำอธิบายและตัวอย่างโค้ด (อย่างง่าย) ใน PHP:

1. Singleton Pattern (Creational)

ปัญหา: ต้องการให้มี Object เพียงตัวเดียวของ Class นั้นๆ และต้องการให้สามารถเข้าถึง Object นั้นได้จากทุกที่ในโปรแกรม

แนวทาง: สร้าง Class ที่มี Constructor เป็น `private` เพื่อป้องกันการสร้าง Object จากภายนอก และมี Static Method ที่ทำหน้าที่สร้างและคืนค่า Object ตัวเดียวของ Class นั้น

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

    private function __construct() {
        // Connect to database (example)
        $this->connection = new PDO("mysql:host=localhost;dbname=mydb", "user", "password");
    }

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

        return self::$instance;
    }

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

// Usage
$db1 = Database::getInstance();
$db2 = Database::getInstance();

// $db1 และ $db2 คือ Object ตัวเดียวกัน
var_dump($db1 === $db2); // Output: bool(true)

เหตุผลเชิงออกแบบ: Singleton Pattern เหมาะสำหรับกรณีที่ต้องการควบคุมการเข้าถึงทรัพยากรที่มีจำกัด เช่น Database Connection หรือ Configuration File

Trade-offs: อาจทำให้เกิดปัญหาในการทดสอบ (Unit Testing) และอาจละเมิด Single Responsibility Principle (SRP) หาก Class Singleton ทำหน้าที่หลายอย่างเกินไป

2. Factory Pattern (Creational)

ปัญหา: ต้องการสร้าง Object โดยไม่ต้องระบุ Class ที่จะสร้างโดยตรง

แนวทาง: สร้าง Factory Class ที่มีหน้าที่ในการสร้าง Object ตามเงื่อนไขที่กำหนด

interface Animal {
    public function makeSound(): string;
}

class Dog implements Animal {
    public function makeSound(): string {
        return "Woof!";
    }
}

class Cat implements Animal {
    public function makeSound(): string {
        return "Meow!";
    }
}

class AnimalFactory {
    public static function createAnimal(string $type): Animal {
        switch ($type) {
            case 'dog':
                return new Dog();
            case 'cat':
                return new Cat();
            default:
                throw new InvalidArgumentException("Invalid animal type: " . $type);
        }
    }
}

// Usage
$dog = AnimalFactory::createAnimal('dog');
echo $dog->makeSound(); // Output: Woof!

$cat = AnimalFactory::createAnimal('cat');
echo $cat->makeSound(); // Output: Meow!

เหตุผลเชิงออกแบบ: Factory Pattern ช่วยลดการพึ่งพา (Coupling) ระหว่าง Class ที่เรียกใช้ (Client) กับ Class ที่ถูกสร้าง (Concrete Classes) ทำให้ Client ไม่จำเป็นต้องรู้รายละเอียดเกี่ยวกับการสร้าง Object

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

3. Strategy Pattern (Behavioral)

ปัญหา: ต้องการเลือก Algorithm หรือพฤติกรรมที่แตกต่างกันใน Runtime

แนวทาง: สร้าง Interface สำหรับ Algorithm และ Implement Algorithm แต่ละแบบใน Class ที่แตกต่างกัน จากนั้นให้ Client เลือก Algorithm ที่ต้องการใช้ผ่าน Interface

interface PaymentStrategy {
    public function pay(float $amount): string;
}

class CreditCardPayment implements PaymentStrategy {
    private $cardNumber;
    private $expiryDate;
    private $cvv;

    public function __construct(string $cardNumber, string $expiryDate, string $cvv) {
        $this->cardNumber = $cardNumber;
        $this->expiryDate = $expiryDate;
        $this->cvv = $cvv;
    }

    public function pay(float $amount): string {
        return "Paid $" . $amount . " using Credit Card: " . $this->cardNumber;
    }
}

class PayPalPayment implements PaymentStrategy {
    private $email;

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

    public function pay(float $amount): string {
        return "Paid $" . $amount . " using PayPal: " . $this->email;
    }
}

class ShoppingCart {
    private $paymentStrategy;

    public function setPaymentStrategy(PaymentStrategy $paymentStrategy) {
        $this->paymentStrategy = $paymentStrategy;
    }

    public function checkout(float $amount): string {
        return $this->paymentStrategy->pay($amount);
    }
}

// Usage
$cart = new ShoppingCart();

$creditCardPayment = new CreditCardPayment("1234-5678-9012-3456", "12/24", "123");
$cart->setPaymentStrategy($creditCardPayment);
echo $cart->checkout(100.00); // Output: Paid $100 using Credit Card: 1234-5678-9012-3456

$payPalPayment = new PayPalPayment("test@example.com");
$cart->setPaymentStrategy($payPalPayment);
echo $cart->checkout(50.00); // Output: Paid $50 using PayPal: test@example.com

เหตุผลเชิงออกแบบ: Strategy Pattern ช่วยให้เราแยก Algorithm ออกจาก Client ทำให้ Client สามารถเปลี่ยน Algorithm ได้โดยไม่ต้องแก้ไขโค้ด

Trade-offs: อาจเพิ่มจำนวน Class ในโปรเจกต์ และต้องระบุ Algorithm ที่ต้องการใช้ใน Client

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

ในบทความนี้เราได้เรียนรู้เกี่ยวกับ Design Patterns และตัวอย่างบางส่วนที่สำคัญ ในบทความถัดไป เราจะลงลึกในเรื่องของการทดสอบ Code OOP ด้วย Unit Testing เพื่อให้มั่นใจว่า Code ของเราทำงานได้อย่างถูกต้องและมีคุณภาพ

Office.in.th Custom Service

想基于此结构开发或定制系统?

Office.in.th 承接企业级 Web Application、API Integration 以及政府/企业管理系统的设计与开发,并提供长期支持。

查看软件开发服务