Dependency Injection và Service Container trong Laravel
Service Container trong Laravel như là trung tâm của một ứng dụng, có mặt mọi nơi trong dự án, điều phối các hoạt động. Trước khi đi vào tìm hiểu Service Container là gì. Chúng ta cũng xem qua vài khái niệm cơ bản có liên quan.
Denpendency Inversion: là một nguyên lý thiết kế và viết code.
Inversion of Control: (IoC) Đây là một design partern nằm trong nguyên lý SOLID, nó được tạo ra để tuân thủ theo nguyên lý Denpendency Inversion. Có rất nhiều cách để thực hiện partern này, Dependency Inversion là một trong số những cách đó.
Dependency Injection: Nếu một Class A hoạt động phụ thuộc vào một vài Class khác, thì thay vì khởi tạo các instance của các Class kia bên trong Class A, ta sẽ inject những instance đó vào thông qua __construct
hay setter
. Những instance của các Class mà Class A cần để hoạt động đó được gọi là dependency. Ví dụ:
class Animal { public $leg; public function __contruct($leg) { $this->leg = $leg; } public function getLegs() { return 'Animal has ' . $this->leg . 'legs'; } } class Human { public $animal; public function __construct($leg) { $this->animal = new Animal($leg); } public function leg() { $this->animal->getLegs(); } } $human = new Human(2); $human->leg();
Ví dụ trên, class Human đã bị phụ thuộc vào class Animal, vi phạm nguyên tắc Denpendency Inversion. Ta sẽ áp dụng Dependency Injection để cải thiện lại như sau:
class Animal { public $leg; public function __contruct($leg) { $this->leg = $leg; } public function getLegs() { return 'Animal has ' . $this->leg . 'legs'; } } class Human { public $animal; public function __construct(Animal $animal) { $this->animal = $animal; } public function leg() { $this->animal->getLegs(); } } $human = new Human(new Animal(2)); $human->leg();
Với IoC thì ta sẽ gửi cho những đoạn xử lý logic đó những thứ mà nó cần. Hay nói cách khác: Đừng cố gọi khắp nơi để tạo ra những thứ mà bạn cần (dependency), chúng tôi sẽ đưa chúng cho bạn khi chúng tôi cần đến bạn!
Ưu điểm của DI
- Giảm sự phụ thuộc giữa các class, các module.
- Code dễ bảo trì, dễ thay thế.
- Giảm sự kết dính giữa các 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)
Nhược điểm của DI
- 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.
Service Container
Service Container trong Laravel là một nơi quản lý class dependency và thực hiện dependency injection. Container dùng bind để tìm và khởi tạo một instance của đối tượng.
Mình giới thiệu một vài cách binding một class vào ứng dụng Laravel:
Simple Bindings
Bên trong một service provider, bạn luôn luôn có quyền truy cập vào trong container thông qua thuộc tính $this->app. Chúng ta có thể đăng kí liên kết sử dụng phương thức bind, và truyền vào tên của class hay interface mà chúng ta muốn đăng kí cùng với Closure thực hiện trả về instance của class đó:
$this->app->bind('MyHelper\API', function ($app) { return new MyHelper\API($app->make('HttpClient')); });
Lưu ý rằng chúng ta nhận được container như một đối số truyền vào cho resolver. Sau đó thì chúng ta có thể thực hiện resolve các sub-dependencies con của đối tượng mà đang được xây dựng.
Binding A Singleton Phương thức singleton thực hiện liên kết một class hoặc interface vào container mà chỉ cần thực hiện duy nhất một lần. Khi một singleton binding được giải quyết, cùng một object instance sẽ được trả về trong các subsequent calls vào container:
$this->app->singleton('MyHelper\API', function ($app) { return new MyHelper\API($app->make('HttpClient')); });
Binding Instances
Bạn cũng có thể bind một instance đang tồn tại vào trong container bằng cách sử dụng phương thức instance. Instance đó sẽ luôn luôn được trả về trong các lần gọi sau vào container:
$api = new MyHelper\API(new HttpClient);
$this->app->instance(‘MyHelper\Api’, $api);
Binding Interfaces To Implementations
Một tính năng khá mạnh của Service Container trong Laravel là nó có khả năng bind một interface thành một implementation. Ví dụ, giả sử chúng ta có interface UserInterface và một implementation UserRepository. Khi đã có code của implementation UserInterface cho interface, chúng ta có thể đăng ký nó với service container như sau:
$this->app->bind( 'App\Contracts\UserInterface', 'App\Repositories\UserRepository' );
Đây là một vài cách bind class của Service Container trong Laravel, còn bind ở đâu và thực hiện chi tiết như nào thì chúng ta cần tìm hiểu về Service Provider để có thể hiểu rõ hơn. Bài sau mình sẽ giới thiệu về Service Provider.
Khá khó để hiểu ngay nếu chưa có kiến thức về lập trình.hic
Đúng rồi bạn. Bạn nào level trung bình trở lên hoặc có kinh nghiệm rồi mới hiểu các khái niệm này.