PlusMagi's Blog By Pitt Phunsanit Backend PHP: การใช้ Dependency Injection (DI) แทนการสร้าง Object ภายในคลาส

PHP: การใช้ Dependency Injection (DI) แทนการสร้าง Object ภายในคลาส

ในโลกของการพัฒนาซอฟต์แวร์ระดับ Enterprise การสร้างระบบที่ยืดหยุ่น (Flexible) และสามารถทดสอบได้ง่าย (Testable) เป็นสิ่งสำคัญอย่างยิ่งสำหรับนักพัฒนา PHP ระดับ Senior Architect แนวคิดหลักที่ช่วยให้เราบรรลุเป้าหมายนี้คือ Dependency Injection (DI) ซึ่งเป็นรูปแบบการออกแบบ (Design Pattern) ที่ทรงพลัง DI ไม่ใช่แค่ไลบรารี แต่เป็นหลักการที่ช่วยให้เราหลีกเลี่ยงปัญหาที่เรียกว่า Tight Coupling หรือการพึ่งพาที่แน่นหนา


💡 ปัญหาของ Tight Coupling ใน PHP

Tight Coupling เกิดขึ้นเมื่อคลาสหนึ่งผูกติดตัวเองกับรายละเอียดการทำงานของคลาสอื่นมากเกินไป ตัวอย่างที่พบบ่อยที่สุดคือการที่คลาสทำการสร้าง Object ที่มันต้องใช้เองภายใน Constructor หรือ Method เช่น:

PHP

class ProductService {<br>  private PostgreSQLRepository $repository;<br>  <span style="color: red;">$repository = new PostgreSQLRepository('host', 'user');</span> // Hardcoded dependency<br>  <br>  public function getProducts() {<br>    return $this->repository->fetchProducts();<br  }<br>}<br>

ในตัวอย่างนี้ ProductService ถูกผูกติดกับ PostgreSQLRepository อย่างแน่นหนา หากวันหน้าเราต้องการเปลี่ยนไปใช้ระบบฐานข้อมูลอื่น (เช่น MySQL) หรือต้องการจำลอง (Mock) Repository เพื่อการทดสอบ (Testing) เราจำเป็นต้องเข้าไปแก้ไขโค้ดภายใน ProductService ซึ่งเป็นการละเมิดหลักการ Open/Closed Principle (OCP) อย่างชัดเจน

🔄 Dependency Injection: การส่งผ่านการพึ่งพา

DI คือการที่คลาสไม่ได้สร้าง Object ที่ตัวเองต้องใช้ แต่ให้ ภายนอก (เช่น Container หรือ Constructor) เป็นผู้ส่งมอบ (Inject) Object นั้นเข้ามาให้แทน หลักการนี้ทำให้ ProductService ไม่ต้องรู้ว่ามันกำลังคุยกับ Repository ตัวจริง หรือ Repository ที่ถูกจำลองขึ้นมาเพื่อทดสอบ

🛠️ การปรับปรุงโค้ดด้วย DI (The Solution)

เราจะแก้ไขโดยการกำหนดให้ ProductService รับ Dependency ผ่าน Constructor และกำหนดให้ใช้ Interface แทนการใช้ Class จริง

PHP

// 1. กำหนด Interface เพื่อให้เกิดความยืดหยุ่น<br>interface ProductRepositoryInterface {<br>  public function fetchProducts(): array;<br>}<br><br>// 2. Implement Class จริง (PostgreSQL) ตาม Interface<br>class PostgreSQLRepository implements ProductRepositoryInterface {<br>  public function fetchProducts(): array {<br>    // โค้ดเชื่อมต่อ PostgreSQL จริง<br>    return ['apple', 'banana', 'orange'];<br  }<br>}<br><br>// 3. ProductService รับ Dependency ผ่าน Constructor (DI)<br>class ProductService {<br>  private ProductRepositoryInterface $repository;<br>  <span style="color: green;">public function __construct(ProductRepositoryInterface $repository) {</span><br>    $this->repository = $repository;<br  }<br>  <br>  public function getProducts(): array {<br>    return $this->repository->fetchProducts();<br  }<br>}<br><br>// วิธีการใช้งาน (Injection Point)<br>$repository = new PostgreSQLRepository();<br>$service = new ProductService($repository);<br>$products = $service->getProducts();<br>

เมื่อเราใช้ DI แล้ว ProductService จะสนใจแค่ว่ามันได้รับ Object ที่มีเมธอด fetchProducts() เท่านั้น ไม่สนใจว่า Object นั้นจะมาจาก PostgreSQL, MySQL, หรือแม้แต่ Mock Object ทำให้โค้ดของเรามีความยืดหยุ่นสูงและสามารถทดสอบแต่ละส่วนได้อย่างอิสระ

✅ บทสรุป

การใช้ Dependency Injection คือการยกระดับการออกแบบระบบจากระดับการเขียนโค้ด (Coding) ไปสู่ระดับสถาปัตยกรรม (Architecture) มันช่วยให้โค้ดของเราเป็นไปตามหลักการ SOLID โดยเฉพาะ Liskov Substitution Principle และ Open/Closed Principle ทำให้ระบบ PHP ที่เชื่อมต่อกับ PostgreSQL ของคุณไม่เพียงแต่ทำงานได้ แต่ยังง่ายต่อการบำรุงรักษาและขยายในอนาคตอีกด้วย