Skip to main content

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

System guide Content reviewed

Single Responsibility Principle (SRP): Class ควรมีหน้าที่เดียว

บทความนี้อธิบายถึงหลักการ Single Responsibility Principle (SRP) ซึ่งเน้นว่า class หนึ่งๆ ควรมีเหตุผลในการเปลี่ยนแปลงเพียงอย่างเดียว เพื่อลดความซับซ้อน เพิ่มความ...

Single Responsibility Principle (SRP): Class ควรมีหน้าที่เดียว

ในบทที่แล้ว เราได้เริ่มสำรวจหลักการ SOLID ซึ่งเป็นแนวทางสำคัญสำหรับการออกแบบเชิงวัตถุที่ดี ในบทนี้ เราจะมาเจาะลึกหลักการแรก นั่นคือ Single Responsibility Principle (SRP) ซึ่งเป็นหลักการที่เรียบง่ายแต่ทรงพลัง และมีผลกระทบอย่างมากต่อความสามารถในการบำรุงรักษาและขยายระบบของเรา

ความหมายของ SRP

Single Responsibility Principle (SRP) กล่าวว่า "Class ควรมีเหตุผลในการเปลี่ยนแปลงเพียงอย่างเดียว" ฟังดูเหมือนเป็นนามธรรมใช่ไหมครับ? ลองมาทำความเข้าใจกันให้ลึกซึ้งยิ่งขึ้น

ความหมายก็คือ Class หนึ่งๆ ควรรับผิดชอบต่อการทำงานที่เฉพาะเจาะจงเพียงอย่างเดียว หาก Class นั้นมีหน้าที่หลากหลาย และแต่ละหน้าที่อาจเปลี่ยนแปลงได้ด้วยเหตุผลที่แตกต่างกัน นั่นหมายความว่า Class นั้นกำลังละเมิด SRP อยู่

ทำไมถึงต้องมีหน้าที่เดียว?

  • ลดความซับซ้อน: Class ที่มีหน้าที่เดียวจะเข้าใจง่ายกว่า Class ที่มีหลายหน้าที่
  • เพิ่มความยืดหยุ่น: เมื่อ Class เปลี่ยนแปลงเพียงเพราะหน้าที่เดียว การเปลี่ยนแปลงนั้นจะไม่ส่งผลกระทบต่อส่วนอื่นๆ ของระบบ
  • เพิ่มความสามารถในการนำกลับมาใช้ใหม่: Class ที่มีหน้าที่เดียวมักจะสามารถนำไปใช้ในบริบทอื่นๆ ได้ง่ายกว่า
  • ง่ายต่อการทดสอบ: Class ที่มีหน้าที่เดียวจะง่ายต่อการเขียน Unit Test เพราะเราสามารถโฟกัสไปที่การทดสอบหน้าที่นั้นๆ ได้อย่างเต็มที่

ตัวอย่างการละเมิด SRP

ลองพิจารณา Class User ด้านล่างนี้:

<?php

class User {
    private $name;
    private $email;

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

    public function getName(): string {
        return $this->name;
    }

    public function getEmail(): string {
        return $this->email;
    }

    public function saveToDatabase(): void {
        // โค้ดสำหรับบันทึกข้อมูลผู้ใช้ลงฐานข้อมูล
        // ...
    }

    public function sendWelcomeEmail(): void {
        // โค้ดสำหรับส่งอีเมลต้อนรับ
        // ...
    }
}

?>

Class User นี้มีหน้าที่สองอย่าง:

  1. จัดการข้อมูลผู้ใช้ (ชื่อและอีเมล)
  2. บันทึกข้อมูลผู้ใช้ลงฐานข้อมูลและส่งอีเมล

ปัญหาคืออะไร? สมมติว่าเราต้องการเปลี่ยนวิธีการบันทึกข้อมูลผู้ใช้ลงฐานข้อมูล (เช่น เปลี่ยนจาก MySQL เป็น PostgreSQL) หรือเราต้องการเปลี่ยนรูปแบบของอีเมลต้อนรับ การเปลี่ยนแปลงเหล่านี้จะส่งผลกระทบต่อ Class User ซึ่งหมายความว่า Class นี้มีเหตุผลในการเปลี่ยนแปลงมากกว่าหนึ่งอย่าง นั่นคือการละเมิด SRP

การแก้ไข SRP

เพื่อแก้ไขปัญหาการละเมิด SRP เราสามารถแยกหน้าที่ต่างๆ ออกเป็น Class ที่รับผิดชอบเฉพาะด้านได้ ตัวอย่างเช่น เราสามารถสร้าง Class UserRepository เพื่อจัดการกับการบันทึกข้อมูลผู้ใช้ลงฐานข้อมูล และ Class EmailService เพื่อจัดการกับการส่งอีเมล

<?php

class User {
    private $name;
    private $email;

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

    public function getName(): string {
        return $this->name;
    }

    public function getEmail(): string {
        return $this->email;
    }
}

class UserRepository {
    public function save(User $user): void {
        // โค้ดสำหรับบันทึกข้อมูลผู้ใช้ลงฐานข้อมูล
        // ...
    }
}

class EmailService {
    public function sendWelcomeEmail(User $user): void {
        // โค้ดสำหรับส่งอีเมลต้อนรับ
        // ...
    }
}

?>

ตอนนี้ Class User รับผิดชอบเพียงการจัดการข้อมูลผู้ใช้, Class UserRepository รับผิดชอบการบันทึกข้อมูลลงฐานข้อมูล, และ Class EmailService รับผิดชอบการส่งอีเมล แต่ละ Class มีเหตุผลในการเปลี่ยนแปลงเพียงอย่างเดียว

Trade-offs และแนวปฏิบัติที่ดี

การนำ SRP ไปใช้อาจทำให้จำนวน Class ในระบบของเราเพิ่มขึ้น ซึ่งอาจทำให้โค้ดดูซับซ้อนขึ้นในตอนแรก อย่างไรก็ตาม ข้อดีที่ได้คือความยืดหยุ่นและความสามารถในการบำรุงรักษาที่ดีขึ้นในระยะยาว

ต่อไปนี้เป็นแนวปฏิบัติที่ดีในการนำ SRP ไปใช้:

  • พิจารณาหน้าที่ของ Class อย่างรอบคอบ: ถามตัวเองว่า Class นี้มีเหตุผลในการเปลี่ยนแปลงมากกว่าหนึ่งอย่างหรือไม่
  • แยกหน้าที่ที่แตกต่างกันออกเป็น Class ใหม่: หาก Class มีหลายหน้าที่ ให้แยกหน้าที่เหล่านั้นออกเป็น Class ที่รับผิดชอบเฉพาะด้าน
  • ใช้ Dependency Injection (DI): เมื่อ Class หนึ่งต้องการใช้บริการของอีก Class หนึ่ง ให้ใช้ DI เพื่อให้ Class นั้นไม่ต้องขึ้นอยู่กับ Concrete Implementation โดยตรง (เราจะพูดถึง DI ในรายละเอียดในบทต่อๆ ไป)
  • อย่ากลัวที่จะสร้าง Class เล็กๆ: Class ที่มีหน้าที่เดียวมักจะมีขนาดเล็ก ซึ่งเป็นเรื่องที่ดี

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

สรุป

Single Responsibility Principle (SRP) เป็นหลักการที่สำคัญสำหรับการออกแบบเชิงวัตถุที่ดี โดยการทำให้ Class มีหน้าที่เดียว เราสามารถลดความซับซ้อน เพิ่มความยืดหยุ่น และเพิ่มความสามารถในการบำรุงรักษาระบบของเราได้ ในบทต่อไป เราจะมาเรียนรู้เกี่ยวกับหลักการ SOLID ข้อที่สอง นั่นคือ Open/Closed Principle (OCP)

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