วันเสาร์ที่ 22 กุมภาพันธ์ พ.ศ. 2568

[PHP] SOLID Principles เขียนโค้ดยังไงให้ Maintain ง่าย

SOLID Principles คืออะไร?

ถ้าคุณเป็น PHP Developer ที่ต้องการเขียนโค้ดให้ดีขึ้น มีโครงสร้างที่ชัดเจน และง่ายต่อการดูแล (maintain) SOLID Principles คือแนวคิดที่คุณต้องรู้!

SOLID เป็นชุดของ 5 หลักการออกแบบเชิงวัตถุ (OOP) ที่ช่วยให้โค้ดของคุณ อ่านง่าย, ขยายได้, และลดบั๊กในอนาคต โดยคำว่า SOLID มาจากอักษรตัวแรกของ 5 หลักการดังนี้:

  • S – Single Responsibility Principle (SRP)
  • O – Open/Closed Principle (OCP)
  • L – Liskov Substitution Principle (LSP)
  • I – Interface Segregation Principle (ISP)
  • D – Dependency Inversion Principle (DIP)


ในบทความนี้ เราจะมาดูกันว่าแต่ละหลักการทำงานยังไง และเราจะใช้มันกับ PHP ได้ยังไง


1. Single Responsibility Principle (SRP) - หนึ่งคลาส หนึ่งหน้าที่

หลักการ:

    "A class should have only one reason to change."
    (คลาสควรมีเหตุผลเดียวที่ต้องเปลี่ยนแปลง)

แปลว่า: คลาสหนึ่งควรมีหน้าที่เดียว ไม่ควรทำหลายอย่างในที่เดียวกัน

❌ ตัวอย่างที่ไม่ดี:

class User {
    public function saveToDatabase() {
        // บันทึกข้อมูลลง Database
    }

    public function sendEmail() {
        // ส่งอีเมลแจ้งเตือน
    }
}

User ควรรับผิดชอบเฉพาะข้อมูลของผู้ใช้ ไม่ควรจัดการเรื่อง Database หรือ Email

✅ วิธีแก้:

class UserRepository {
    public function save(User $user) {
        // บันทึกข้อมูลลง Database
    }
}

class EmailService {
    public function send(User $user) {
        // ส่งอีเมลแจ้งเตือน
    }
}

ข้อดีของ SRP:

  • แยกโค้ดตามหน้าที่ ทำให้แก้ไขง่าย
  • ลดผลกระทบเมื่อเปลี่ยนแปลงโค้ด



2. Open/Closed Principle (OCP) - เปิดสำหรับขยาย ปิดสำหรับแก้ไข

หลักการ:

    "Software entities should be open for extension but closed for modification."
    (โค้ดควรเปิดให้ขยาย แต่ปิดสำหรับการแก้ไข)

แปลว่า: เราควรออกแบบโค้ดให้เพิ่มฟีเจอร์ใหม่ได้โดยไม่ต้องแก้ไขโค้ดเก่า

❌ ตัวอย่างที่ไม่ดี:

class Payment {
    public function pay($method) {
        if ($method == "credit_card") {
            // จ่ายผ่านบัตรเครดิต
        } elseif ($method == "paypal") {
            // จ่ายผ่าน PayPal
        }
    }
}

ถ้าอยากเพิ่มวิธีชำระเงินใหม่ เราต้องไปแก้ไขโค้ดเดิม ซึ่งไม่ดีเลย

✅ วิธีแก้: ใช้ Polymorphism

interface PaymentMethod {
    public function pay();
}

class CreditCardPayment implements PaymentMethod {
    public function pay() {
        // จ่ายผ่านบัตรเครดิต
    }
}

class PayPalPayment implements PaymentMethod {
    public function pay() {
        // จ่ายผ่าน PayPal
    }
}

class PaymentProcessor {
    public function process(PaymentMethod $paymentMethod) {
        $paymentMethod->pay();
    }
}


ข้อดีของ OCP:

  • เพิ่มวิธีชำระเงินใหม่ได้ง่าย โดยไม่ต้องแก้โค้ดเดิม
  • ลดโอกาสเกิดบั๊กจากการแก้ไขโค้ด


3. Liskov Substitution Principle (LSP) - ใช้แทนกันได้โดยไม่พัง

หลักการ:

    "Objects of a derived class must be substitutable for objects of the base class."
    (คลาสลูกควรใช้แทนคลาสแม่ได้โดยไม่ทำให้โปรแกรมพัง)

❌ ตัวอย่างที่ไม่ดี:

class Bird {
    public function fly() {
        return "บินไปเลย!";
    }
}

class Penguin extends Bird {
    public function fly() {
        throw new Exception("เพนกวินบินไม่ได้!");
    }
}

Penguin เป็น Bird แต่ดันบินไม่ได้ ทำให้โปรแกรมอาจพังเมื่อเรียก fly()

✅ วิธีแก้: แยก Bird ออกเป็น FlyingBird และ NonFlyingBird

abstract class Bird {}

class FlyingBird extends Bird {
    public function fly() {
        return "บินไปเลย!";
    }
}

class Penguin extends Bird {
    public function swim() {
        return "ว่ายน้ำ!";
    }
}


ข้อดีของ LSP:

  • ใช้คลาสลูกแทนคลาสแม่ได้อย่างปลอดภัย
  • ลดปัญหาบั๊กที่เกิดจากพฤติกรรมที่ไม่คาดคิด


4. Interface Segregation Principle (ISP) - ใช้ Interface ให้เหมาะสม

หลักการ:

    "Clients should not be forced to depend on methods they do not use."
    (อย่าทำให้คลาสต้องพึ่งพาเมธอดที่มันไม่ได้ใช้)

❌ ตัวอย่างที่ไม่ดี:

interface Worker {
    public function work();
    public function eat();
}

class Robot implements Worker {
    public function work() {
        return "ทำงาน!";
    }

    public function eat() {
        throw new Exception("หุ่นยนต์ไม่กินข้าว!");
    }
}

✅ วิธีแก้: แยก Interface

interface Workable {
    public function work();
}

interface Eatable {
    public function eat();
}

class Robot implements Workable {
    public function work() {
        return "ทำงาน!";
    }
}

ข้อดีของ ISP:

  • ลดการใช้ method ที่ไม่จำเป็น
  • คลาสลูก implement interface เท่าที่จำเป็น


5. Dependency Inversion Principle (DIP) - แยกส่วนการพึ่งพาออกจากกัน

หลักการ:

    "Depend on abstractions, not concretions."
    (ควรพึ่งพา abstraction มากกว่าการอ้างอิงถึง class โดยตรง)

❌ ตัวอย่างที่ไม่ดี:

class MySQLDatabase {
    public function connect() {
        return "เชื่อมต่อ MySQL!";
    }
}

class UserRepository {
    private $db;

    public function __construct() {
        $this->db = new MySQLDatabase(); // ❌ ผูกกับ MySQL โดยตรง
    }
}

✅ วิธีแก้: ใช้ Dependency Injection

interface Database {
    public function connect();
}

class MySQLDatabase implements Database {
    public function connect() {
        return "เชื่อมต่อ MySQL!";
    }
}

class UserRepository {
    private $db;

    public function __construct(Database $db) {
        $this->db = $db;
    }
}

ข้อดีของ DIP:

  • เปลี่ยน Database ได้ง่าย
  • ลดการพึ่งพาของ class




SOLID Principles ช่วยให้โค้ด PHP ของเรามีโครงสร้างที่ดีขึ้น แก้ไขและขยายโค้ดได้ง่าย ลดบั๊กที่อาจเกิดขึ้น ถ้าคุณต้องการเป็น PHP Developer ที่เก่งขึ้น SOLID เป็นแนวคิดที่คุณควรรู้!

ไม่มีความคิดเห็น:

แสดงความคิดเห็น