Dependency Injection và DI Containers
Bài đăng này đã không được cập nhật trong 3 năm
Với cách viết code thông thường xảy ra tình huống các class cấp cao gọi class cấp. Như vậy class cấp cao phụ thuộc vào class cấp thấp. Nghĩa là khi class cấp thấp thay đổi thì class cấp cao phải thay đổi theo. Dần dần sự thay đổi trở nên chồng chéo và gây khó khăn cho bảo trì source code chương trình. Để hiểu Dependency Injection là gì, mình dùng lại ví dụ trong bài Tight-coupling và loose-coupling giữa các object trong Java. như sau.
public class Travel {
Car car = new Car();
void startJourney() {
car.travel();
}
}
public class Car {
void move() {
System.out.prinln("Move by Car");
}
}
public class Train {
void move() {
System.out.prinln("Move by Train");
}
}
Ta nhận thấy rằng class Travel phụ thuộc chặt chẽ (dependent) vào class Car (hay class Car dependency class Travel). Khi class Car thay đổi thì class Travel cũng thay đổi theo. Đặc biệt khi một class khác gọi phương thức startJourney
muốn "di chuyển" bằng phương tiện Train thì buộc ta phải thay đổi souce code của Travel.
public clas Travel {
Train train = new Train();
void startJourney() {
train.travel();
}
}
Để loại bỏ sự phụ thuộc cứng của class Travel vào class Car hay Train, một cách tương tự bài viết trước và cũng giải quyết được vấn đề.
public interface Vehicle {
void move();
}
public class Car implements Vehicle {
@Override
void move() {
System.out.prinln("Move by Car");
}
}
public class Train implements Vehicle {
@Override
void move() {
System.out.prinln("Move by Train");
}
}
public clas Travel {
Vehicle v;
public Travel(Vehicle v) {
this.v = v;
}
void startJourney() {
v.move();
}
}
Giờ đây class Travel chỉ phụ thuộc vào Vehicle nhưng, sự phụ thuộc này không còn "cứng" nữa mà ta có thể truyền vào bất kì một impliment nào của Vehicle.
1. Dependency Injection là gì
Vậy Dependency Injection có thể hiểu đơn giản là cách thức tổ chức, design source code sao cho có thể "tiêm" các đối tượng dependency vào trong đối tượng mà nó dependent. Các phương pháp cơ bản để Dependency Injection.
- Constructor Injection: Các dependency sẽ được 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.
- Setter Injection: Các dependency sẽ được truyền vào 1 class thông qua các hàm Setter.
- Public fields: Các dependency sẽ được truyền vào 1 class một cách trực tiếp vào các public field. Cách này ít được sử dụng nhất.
Ưu điểm
- Làm giảm (khử) sự phụ thuộc giữa các object: Các object không giao tiếp trực tiếp với nhau mà thông qua Interface.
- Dễ viết unit test: Dễ hiểu là khi ta có thể Inject các dependency vào trong một class thì ta cũng dễ dàng "tiêm" các mock object vào class (được test) đó.
- Dễ dàng thấy các quan hệ giữa các object: Dependency Injection sẽ "tiêm" các object phụ thuộc vào các interface thành phần của object bị phụ thuộc nên ta dễ dàng thấy được các dependency của một object. Nhược điểm
- Khó debug: Vì sửa dụng các interface nên có thể gặp khó khăn khi ta debug source code (bằng mắt) vì không biết implement nào thực sự được truyền vào.
- Ẩn lõi khi comlile: Vì 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.
2. DI containers
Ngày nay có nhiều DI containers được sử dụng. Phổ biến trong Java là Spring, ButterFly, Seasar framework... Có thể hiểu một cách đơn giản DI containers là một phần quan trọng của một framework. Nó như một "thùng" để chứa các component dependency của ứng dụng, của project. Một minh họa khá đơn giản DI containers trong Spring framework. Trong đó POJOs là những class (object) java thuần. Configuration Metadata là các file config ta sẽ khai báo các component dependency nào sẽ tạo instance và "lưu" vào container khi ứng dụng start.
Tài liệu tham khảo
All rights reserved