Dependency injection và dependency injection container trong Laravel

Bùi Huy Cường
4 min readSep 11, 2020

Mọi thứ trong cuộc sống nói chung và lập trình nói riêng đều được thiết kế sao cho đơn giản và dễ dàng nâng cấp, bảo trì và vận hành nó. Những giải pháp hay thường đến từ những vấn đề khó gặp phải trong lúc ta làm điều gì đó. Những Design Pattern chúng ta được học là những thứ đã xảy ra trong thực tế và nó được phát minh ra từ đó, Dependency Injection(Structural) cũng vậy.

Định nghĩa

Dependency Injection(DI) là mô hình lập trình, cách tổ chức code sao cho các đoạn code khác nhau, các module khác nhau, các lớp khác nhau không phụ thuộc nhau một cách cứng nhắc, mà cần có một cơ chế thay đổi các thành phần phụ thuộc cả ở thời điểm chạy và thời điểm biên dịch.

Lợi ích

- Làm giảm sự phụ thuộc giữa các đối tượng
- Dễ viết bảo trì, test, module hóa ứng dụng và mở rộng

Nhược điểm

- Dependency Injection ẩn các dependency nên một số lỗi chỉ xảy ra khi chạy chương trình thay vì có thể phát hiện khi biên dịch chương trình.

Ví dụ

class Config {
private $username;
private $password;
public function __construct($username, $password) {
$this->username = $username;
$this->password = $password;
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
}
// C1. Không sử dụng DI
class Database
{
private $config;
public function __construct($username, $password) {
$this->config = new Config($username, $password);
}
public function getConfig() {
return $this->config;
}
}
$database = new Database('buihuycuong', 'ratdeptrai');
var_dump($database->getConfig());
// C2. Sử dụng DI
class Database
{
private $config;
public function __construct(Config $config) {
$this->config = $config;
}
public function getConfig() {
return $this->config;
}
}
$config = new Config('buihuycuong', 'ratdeptrai');
$database = new Database($config);
var_dump($database->getConfig());
// C3. Sử dụng DI trong setter
class Database
{
private $config;
public function __construct() {
}
public function getConfig() {
return $this->config;
}
public function setConfig(Config $config) {
$this->config = $config;
}
}
$config = new Config('buihuycuong', 'ratdeptrai');
$database = new Database();
$database->setConfig($config);
var_dump($database->getConfig());
// C4. Sử dụng DI thông qua Interfaceinterface DatabaseInterface {
public function getConfig();
public function setConfig(Config $config);
}

Trong cách 1 ở trên ta không sử dụng DI sẽ có một số vấn đề là ta phải khởi tạo Config ngay trong class Database, về logic thì không có gì để nói, nhưng về mở rộng và bảo trì code thì sẽ khó bởi:
- Database và Config class đang bị gắn vào nhau, nếu Config có sự thay đổi(về tham số khởi tạo chẳng hạn) thì sẽ kéo theo sự thay đổi của Database và những class liên quan tới Config.
- Database biết nhiều thứ của Config quá, mà cái gì quá cũng không tốt :D

Với cách thứ 2 thì Database không phụ thuộc vào Config nữa, mà được truyền thẳng đối tượng Config thông qua hàm khởi tạo, như vậy sẽ giải quyết được vấn đề ở cách 1 thực hiện.

Các kiểu Dependency Injection

1. Constructor Injection
Ở cách thứ 2 chính là sử dụng kiểu Constructor Injection, với cách này có một số đặc điểm là giảm sự phụ thuộc, giảm sự phức tạp từ Config và linh hoạt trong việc mở rộng và thay đổi từ Config.

2. Setter Injection
Cái này là ở trong cách thứ 3. Như vậy ta cũng không cần làm thay đổi logic trong code mà chỉ đơn giản khai báo trong setter, đôi khi chúng ta cũng cần một giá trị mặc định gì đó cho Config thì có thể triển khai trong setter này.

3. Interface Injection
Sử dụng như trong cách thứ 4. Khi Database cần triển khai thì cần implements DatabaseInterface và cần cung cấp đối tượng Config thông qua setConfig.

Dependency Injection Container

Khi đối tượng phụ thuộc quá lớn thì sao?

Lúc này giải pháp đưa ra là cần tạo một thùng chứa chuyên quản lý tất cả các đối tượng độc lập, từ autoload, khởi tạo, thiết lập và cài cắm vào đối tượng khác. Dependency Injection Container được ra đời.

Trong Laravel có một công cụ rất mạnh đó là Service Container, nó quản lý các dependencies và thực hiện xử lý dependency injection. Các bạn có thể tham khảo thêm trong tài liệu của Laravel.

Tài liệu tham khảo:

--

--