Hi! Xin chào mọi người, lại là mình đây. Sau 1 thời lan lú đú ở TypeScript thì mình đã quay lại tìm hiểu với Laravel. Hẳn đã code một thời gian nhưng mấy cái khái niệm thực sự ít ai để ý với một newbie. Sau đây mình sẽ tổng hợp ngắn gọn Dependency Injection & IoC Container
trong Laravel là như thế nào nhé ,, gét gô 👌:
Dependency Injection là một design pattern tuyệt vời khi chúng ta cần thiết kế 1 chương trình có sự linh hoạt, mềm dẻo ,dễ thay đổi ,dễ dàng mở rộng code và giảm sự phụ thuộc giữa các dependency với nhau.
1. Dependency Injection
1.1 Dependency là gì ?
Class A dependency Class B là hành vi Class A sử dụng các thuộc tính và phương thức của class B .
Về nguyên tắc lập trình, khi Class A sử dụng method của Class B thì trước tiên phải có instance
của Class B.
Ví dụ : Tạo Class Student, mỗi 1 Student tham gia vào các Group .Các tấm chiếu mới thì sẽ thường làm như thế này :
class Group {
$private $groupName;
public function __construct($groupName)
{
$this->groupName = $groupName;
}
}
class Student {
private $age;
private $name;
private $group;
public function __construct($age, $name, $groupName)
{
$this->age = $age;
$this->name = $name;
$this->group = new Group($groupname);
}
}
Vậy nếu như Class Group 1 lúc nào đó cần thêm 1 thuộc tính $description
thì sao ?. Khi đó constructor
của Class Student phải thêm tham số. Và nếu như chương trình có các Class Teacher , School cũng dependency class
Group thì sao ?. Thì ta phải sửa hết các chỗ có Class Teacher ,School,.... Lúc này tha hồ ngồi dò tìm .
Dependency Injection là 1 Design Pattern giúp giải quyết vấn đề này. Khi thay đổi trong Class, ta chỉ cần thay đổi 1 chỗ thôi.
**1.2 Dependency Injection**
Ý tưởng của nó là tiêm các class dependency
từ bên ngoài vào. Nó truyền vào mà không phải khởi tạo bên trong các method, constructor
. Sẽ tránh bị phụ thuộc cứng vào 1 class dependency
:
class Student{
public function __construct($name, Group $group)
{
$this->name = $name;
$this->group = $group;
}
}
$student = new Student ("Duc Tran", new('Lập trình hướng đối tượng'))
//Object Group được tiêm vào construct Student
Bây giờ thì Group có thay đổi gì thì cũng không phải lo gì nữa, vì instance
của nó đã được truyền vào trong Class Student
. Ta có thể thoái mái sử dụng method ,thuộc tính của Group từ instance class
Group được inject
vào mà không phải lo lắng điều gì cả .
Có 3 kiểu DI :
Constructor Injection : inject
(tiêm) Class
vào constructor
Setter Injection : inject
(tiêm) Class thông qua các hàm Setter
Interface Injection : inject
(tiêm) implement
của interface
Trong 3 loại DI này thì mình đánh giáInterface Injection
là hay nhất. Nó được sử dụng rất phổ biến trong ASP.net. Có nghĩa là ta không cần tiêm trực tiếp Class dependency vào constructor
hay setter
mà ta sẽ tiêm Interface
vào. Điều này có nghĩa là bất cứ class nào implement Interface
đó thì đều có thể được resolve
.
Ưu điểm :
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 : Người lập trình sẽ gặp nhiều khó khăn khi cácdependency class
của họ phụ thuộc tầng tầng lớp lớp vào nhau. Điều đó đã có Inversion of Control
lo lắng .
2. Inversion of Control (Service Container)
Hiểu đơn giản là tất cả các dependency
của chương trình sẽ được tống vào 1 cái kho gọi là IoC container
. Bất kỳ khi nào cần dependency class thì chỉ cần lấy từ trong kho đó, kho đó sẽ tự động khởi tạo class dependency
.
Nếu dependency class của bạn cần những dependency
khác, nó sẽ có khả năng tự động inject
hết cho bạn. Với điều kiện các tham số constructor
đều phải có type-hint
, vì có type-hint
nên container
mới hiểu được là phải lấy dependency
đó từ đâu. Đây là lí do IoC
ưu việt hơnDependency Injection
.
Các framework Spring , Laravel,... sử dụng IoC Container
. Trong Laravel, service Container chính là IoC Container
, và nó cũng chính là instance application
. Ta cũng có thể nói, bản chất instance application laravel
của chúng ta là 1service container
khổng lồ.
Đầu tiên các Class Dependency sẽ được đăng kí trong các Service Provider qua method register
Các Class Dependency sẽ được đưa vào Service Container nhưng các class này chưa được khởi tạo mà chỉ được đăng kí .
Sau khi đăng kí, bất kỳ khi nào cần dependency class
, Service Container sẽ tự động khởi tạo cho chúng ta. Kể cả khi các dependency class
cũng dependency
các class khác thì Service Container cũng sẽ inject
hết dựa vàotype-hint
.
Các Facades aliases đều chứamethod getFacadeAccessor()
. Method này trả về 1 chuỗi là tên cácclass dependecy
sẽ được đăng kí vào container
. Đó là lí do vì sao gọi Facades, Laravel tự động khởi tạo đối tượng .
3. Cách sử dụng Service Container .
- Bind : Chỉ việc đăng ký 1
class
hay 1 interface
với Container. Các dependency
được đăng kí trong method register() trong Service Provider . Bên trong Service Provider thì chúng ta luôn có quyền truy cập vào Container thông qua $this->app, $this->app
là 1 service container (instance application) :
$this->app->bind('key',closure() } );
//Closure là hàm closure sẽ được thực thi khi resolve
//key là khóa để sử dụng khi resolve
$this->app->bind('key', 'nameclass' } );
- Singleton binding : Liên kết một
class
hay interface
vào Container. Như tên gọi của nó thì instance
sẽ chỉ được resolve
một lần, những lần gọi tiếp theo sẽ không tạo ra instance
mới mà chỉ trả về instance
đã được resolve
từ trước :
$this->app->singleton('key', closure());
- Instance binding : Gần giống như Singleton. Bạn có một
instance
và bạn bind
nó vào Service Container. Mỗi lần lấy ra bạn sẽ nhận lại được instance
đó. Nhưng object khởi tạo đầu tiên không phải ở trong Closure mà là ở ngoài, nằm ở trước bind instance
:
$object = new Object();
$this->app->instance('key', $object);
- Interface Binding : Trong Laravel bạn sẽ gặp nhiều những trường hợp như trên khi sử dụng Contracts. Về bản chất thì Contracts chỉ đơn giản là những Interface, và khi bạn
resolve
hay type-hint
chúng ở constructor
hay methods
thì sẽ nhận được Implementation tương ứng đã được đăng ký với Service Container. Cái này tương tự Instance Injection :
$this->app->bind('interfacename', 'nameimplement');
- Tag(cái này ít thấy xài) : Khi có 2 , 3 ,..
class implement
1 interface
, ta có thể gom lại các class
này và bind
vào service container:
$this->app->tag(['nameclass1', 'nameclass2',...], 'nameinterface');
$this->app->tagged('nameinterface'):Resolve 1 mảng các instance class1, class2,....
- Resolve : Lấy ra instance từ Container
- Sử dụng
make
: $instance = $this->app->make('nameclass');
- Truy cập Container như 1 mảng :
$instance = $this->app['nameclass'];
Bài chia sẻ đến đây là hết, thank for reading...😇