Inversion of Control Principle - Nguyên lý thiết kế đảo ngược sự kiểm soát
Khi mới làm quen và tìm hiểu về thiết kế hệ thống, tôi bắt gặp cụm từ Inversion of Control Principle – Nguyên lý đảo ngược sự kiểm soát.
Lúc đó, tôi chỉ biết thốt lên: “Mình đang đọc cái quái gì thế này?”.
Mãi đến gần đây tôi mới thật sự hiểu được nguyên lý này.
Trong bài viết này, tôi sẽ giải thích bằng một ví dụ thực tế để bạn dễ tiếp cận và hiểu tại sao nó lại có cái tên vừa lạ vừa “ngầu” đến vậy 😁.
Tôi vốn rất thích cà phê, nên sáng nào cũng cố thu xếp thời gian để pha cho mình một ly.
Giờ thì mời anh em cùng theo chân tôi pha một ly cà phê nhé.
Để pha cà phê, bạn cần chuẩn bị:
- Cà phê rang xay
- Phin pha cà phê
- Ly
- Nước sôi
Sau khi chuẩn bị đủ, ta cho khoảng 3 muỗng cà phê vào ly, thêm nước sôi, rồi chờ hơn 5 phút để thưởng thức cái thứ nước màu cánh gián, đắng ngắt nhưng thơm phức này.
Nhưng nếu sáng nay tôi muốn uống trà thì sao?
Tôi sẽ phải làm công việc tương tự:
- Trà
- Bình pha trà
- Cốc
- Nước sôi
Pha trà cầu kỳ hơn pha cà phê, nhưng tôi chọn cách nhanh gọn:
Cho trà vào bình, thêm nước sôi, chờ ngấm và mời cả nhà thưởng thức.
Vấn đề: Sự phụ thuộc
Qua ví dụ trên, bạn sẽ thấy:
Để có một ly đồ uống buổi sáng, tôi phụ thuộc vào:
- Dụng cụ
- Nguyên liệu
- Cách pha chế
Nếu muốn thưởng thức loại đồ uống khác, tôi lại phải chuẩn bị toàn bộ từ đầu.
“Ôi giời ơi! Đọc mãi toàn trà với cà phê, IoC đâu mất rồi?”
– Một độc giả sốt ruột 😠
Giải pháp: Đảo ngược sự kiểm soát
Thay vì tự pha, tôi ra quán cà phê.
Ở đó luôn sẵn mọi loại đồ uống tôi cần, tôi không còn phụ thuộc vào việc chuẩn bị nguyên liệu hay dụng cụ.
“Đảo ngược sự kiểm soát” nghĩa là gì?
- Tự pha ở nhà: Khi muốn uống cà phê hay trà, tôi tự bắt đầu quá trình pha chế → Tự khởi tạo đối tượng.
- Ra quán: Thức uống đã được chuẩn bị từ trước (Đối tượng khởi tạo sẵn). Khi khách gọi món, nhân viên chỉ việc mang ra.
➡ Điều này nghĩa là quyền kiểm soát việc khởi tạo đã được đảo ngược — từ tay người dùng sang tay một “bên thứ ba” quản lý (ở đây là quán cà phê).
Trong lập trình
Nguyên lý đảo ngược sự kiểm soát (IoC) thường được áp dụng cùng với Dependency Injection (DI) — một pattern phổ biến để triển khai nguyên lý này.
Ví dụ về IoC
Ví dụ này minh họa cách áp dụng Dependency Injection (DI) trong Java thông qua một mô hình đơn giản liên quan đến quán cà phê.
Các thành phần:
Drink
: Interface định nghĩa hành vi chung của đồ uống.Coffee
vàTea
: Hai lớp triển khai từDrink
, thể hiện hai loại đồ uống khác nhau.CafeShop
: Lớp đại diện cho quán cà phê, có thể phục vụ hai loại đồ uống là coffee, và tea.
Ý nghĩa:
Ví dụ này giúp bạn hiểu rõ cách Dependency Injection:
- Tách biệt sự phụ thuộc giữa các lớp.
- Tăng tính linh hoạt và khả năng mở rộng của ứng dụng.
1
2
3
4
5
6
// Drink.java
public interface Drink {
String getName();
double getPrice();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// Coffee.java
public class Coffee implements Drink {
@Override
public String getName() {
return "Coffee";
}
@Override
public double getPrice() {
return 2.5;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// Tea.java
public class Tea implements Drink {
@Override
public String getName() {
return "Tea";
}
@Override
public double getPrice() {
return 1.8;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// CafeShop.java
public class CafeShop {
private final Drink coffee;
private final Drink tea;
// Constructor Injection
public CafeShop(Drink coffee, Drink tea){
this.coffee = coffee;
this.tea = tea;
}
public void serveDrink(Drink drink) {
System.out.println("Serving: " + drink.getName() + " - Price: $" + drink.getPrice());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Main.java
public class Main {
public static void main(String[] args) {
// Initial object coffee and object tea
Drink coffee = new Coffee();
Drink tea = new Tea();
// Serve drinks via CafeShop
CafeShop cafeShop = new CafeShop(coffee, tea);
cafeShop.serveDrink(coffee);
cafeShop.serveDrink(tea);
}
}