12/08/2018, 15:35

Tự tạo 1 mini Dependencies Injection Container với PHP

Có lẽ rất nhiều người trong chúng ta đã rất quen thuộc với khái niệm Dependencies Injection, cũng như hiệu quả của nó mang lại. Vậy DI hoạt động như thế nào trong PHP và các framework của PHP, để hiểu rõ cách tốt nhất là xây dựng 1 mini DI container dựa theo cách các framework triển khai nó. ...

Có lẽ rất nhiều người trong chúng ta đã rất quen thuộc với khái niệm Dependencies Injection, cũng như hiệu quả của nó mang lại. Vậy DI hoạt động như thế nào trong PHP và các framework của PHP, để hiểu rõ cách tốt nhất là xây dựng 1 mini DI container dựa theo cách các framework triển khai nó. Container chúng ta xây dựng sẽ không hoàn toàn giống và chi tiết như Service Container trong Laravel (chẳng hạn) nhưng chắc chắn sẽ giúp bạn hiểu hơn về cách làm của họ.

Bắt đầu, tôi sẽ tóm lược lại 1 vài những điểm cơ bản nhất về DI và tất nhiên theo 1 cách dễ hiểu nhất, sau đó rất nhanh chúng ta sẽ bắt tay vào xây dựng 1 mini DI Container để ứng dụng ngay. Như thường lệ, code demo minh hoạ nằm ở cuối bài

Dependencies Injection là gì?

Dependencies Injection là 1 cách triển khai để ứng dụng Inversion Of Control Design Pattern vào chương trình.

Các bạn cần phân biệt nó với Dependencies Inversion là 1 nguyên lý của lập trình hướng đối tượng. Trong bài khi viết DI có nghĩa là đang nhắc tới Dependencies Injection

Mục đích của DI

Bắt đầu với 1 ví dụ kinh điển nhưng cũng rất dễ hiểu.

class Car {
    private $engine;
    
    public function __construct()
    {
        $this->engine = new HuyndaiEngine();
    }
}

Ta thấy rằng Car đang bị phụ thuộc vào HuyndaiEngine, và nếu muốn thay đổi động cơ engine như là V6Engine chẳng hạn cho nó, bắt buộc ta phải sửa code. Hoặc giả như sau này HuyndaiEngine có thêm đối số đầu vào thì ta cũng phải sửa lại code của Car cho phù hợp ví dụ như :

class HuyndaiEngine()
{
    private $extra;

    public function __contruct(Nitro $nitro)
    {
        $this->extra = $nitro
    }
}

class Car {
    private $engine;
    
    public function __construct(Nitro $nitro)
    {    
        $this->engine = new HuyndaiEngine($nitro);
    }
}

Vậy thì DI có mặt là để giải quyết problem này hay nói cách khác nó decoupling code của bạn. Các thành phần tương tác với nhau không còn chặt chẽ nữa. Khi sử dụng DI code của bạn ngắn gọn tương tự như sau:

    //Đầu tiên đăng ký Dependencies
    DIContainer->bind('engine', 'HuyndaiEngine');
    
    //Gọi ra thể hiện của Car
    $car = DIContainer->get(Car);

Đoạn code đầu tiên đăng ký Dependencies nếu nói theo human-language như thế này :

DIContainer, khi nào tôi gọi egne từ DIContainer thì đưa cho tôi HuyndaiEngine.

Điều kỳ diệu là khi bạn gọi ra thể hiện của Car thì tất cả các thành phần Car phụ thuộc vào sẽ được DIContainer tính toán và đưa vào cho bạn dựa vào danh sách dependencies đã được đăng ký. Mô hình DIContainer này được các framework triển khai và nếu bạn đang dùng 1 trong số đó thì công việc còn lại chỉ là đăng ký các dependencies và khởi tạo Class, tất cả cứ để DIContainer lo.

1 phần quan trọng trong IoC(Inversion of Control) là các class ràng buộc với nhau qua Interface không phải bằng Implementation, nên trong nhiều framework cách tốt nhất để ứng dụng DI là đăng ký Interface với DI. Khi đó trong class cần dùng (Car) sẽ giao tiếp với Dependencies (HuyndaiEngine) thông qua 1 interface là Engine.

Tất nhiên để nói về ứng dụng của DI thì còn rất rất nhiều như là dễ dàng hơn trong Unit Test hay mở rộng ứng dụng, tái sử dụng code ,... Nhưng để không vượt quá phạm vi của bài viết, tôi xin nhường phần này lại cho các bạn tìm hiểu             </div>
            
            <div class=

0