Dependency Injection trong PHP

Khái niệm liên quan

  • Dependency Injection (DI) là khái niệm thường được nghe trong giới lập trình. Có khá nhiều cái tên nghe liên quan và na ná nhau làm dev bị hoang mang như: Dependency Injection, Inversion of Control, Dependency Inversion, Dependency Injection Container.

  • Các khái niệm trên được hiểu như sau:

    • Dependency Inversion là một nguyên lý để thiết kế và viết code trong 5 nguyên lý S.O.L.I.D do Robert C.Martin viết. Các khái niệm tiếp theo đều được phát triển để thực hiện hóa nguyên lý này.
    • Inversion of Control (IoC) là một design pattern được tạo ra có thể tuân thủ nguyên lý Dependency Inversion trong thiết kế và code. Có nhiều cách hiện thực pattern này: ServiceLocator, Event, Delegate, … Dependency Injection là một trong các cách đó.
    • Dependency Injection là một phương thức để thực hiện design partern Inversion of Control, tức là một phương thức để viết code tốt hơn.
    • Dependency injection container là một tool để giúp DI tốt hơn, đơn giản hơn , phổ biết trong PHP như là PHP-DI, Dice, Symfony-dependency, Pimple ...

Các cách Dependency Injection

Có 3 dạng Dependency Injection:

Constructor Injection

  • Các dependency sẽ được container truyền vào (inject vào) 1 class thông qua constructor của class đó. Đây là cách thông dụng nhất.

class A {
    public $b;

    public function __construct(B $b)
    {
        $this->b = $b;
    }
}

Setter Injection

  • Các dependency sẽ được truyền vào 1 class thông qua các hàm Setter.
class A {
   public $b;

   public function __construct()
   {
      
   }

   public function setB(B $b)
   {
       $this->b = $b;
   }
}

Interface Injection

  • Class cần inject sẽ implement 1 interface. Interface này chứa 1 hàm tên Inject. Container sẽ injection dependency vào 1 class thông qua việc gọi hàm Inject của interface đó. Đây là cách rườm rà và ít được sử dụng nhất.

Ưu điểm và khuyết điểm của DI

Ưu điểm

  • Giảm sự kết dính giữa các module
  • Code dễ bảo trì, dễ thay thế module
  • Rất dễ test và viết Unit Test
  • Dễ dàng thấy quan hệ giữa các module (vì các dependecy đều được inject vào constructor)

Khuyết điểm

  • Khái niệm DI khá khó cho các developer mới
  • Sử dụng interface nên đôi khi sẽ khó debug, do không biết chính xác module nào được gọi
  • Các object được khởi tạo toàn bộ ngay từ đầu, có thể làm giảm performance
  • Làm tăng độ phức tạp của code

Đối vơi dự án lớn thì việc sử dụng DI là cần thiết vì code sẽ linh hoạt, dễ mở rộng và bảo trì. Hiên nay, tất cả các Framework PHP đều dùng DI

Dependency injection container (DI container)

  • Có một vấn đề đặt ra trong DI đó là. Các module inject chia thành nhiều tầng nhiều lớp. Ví dụ module A inject B, B inject C, C inject D, D inject E, F... Khi số lượng module inject lớn thì việc kiểm soát DI rất phức tạp khi khởi tạo đối tượng A. Chúng ta phải biết rõ từng module inject vào phụ thuộc module nào.

$a = new A(new B(new C(new D(new E, new F))));

  • Dependency injection container ra đời để giải quyết vấn đề này. Tức là chúng ta sẽ khởi tạo đối tượng qua DI container mà không cần quan tâm đến các phụ thuộc DI của nó. DI container sẽ tự động inject chính xác các DI cần thiết. Điều này rất tuyệt vời.
  • Ví dụ mình sẽ dùng Dice DI container để khởi tạo một instance A vô cùng đơn giản. Các DI như B, C, D, E, F đã tự động inject rồi.
require_once 'Dice.php';
$dice = new \Dice\Dice;

$a = $dice->create('A');

Kết luận

  • DI là một kĩ thuật tuyệt vời để thiết kết và code rõ ràng, mạch lạc, dễ mở rộng và bảo trì hơn. Việc sử dụng DI container sẽ giúp việc DI dễ dàng và đơn giản hơn. Vậy nên việc ứng dụng DIDI container sẽ giúp chúng ta có những đoạn code pro hơn, nâng cao trình độ hơn.