Tìm hiểu về OCP & DIP trong SOLID | SOLID Series - Viblo
Context:
Giả sử ta đang làm hệ thống thanh toán (Payment) cho một ứng dụng thương mại điện tử.
Ta có nhiều loại thanh toán: CreditCardPayment
, PayPalPayment
, CryptoPayment
, v.v.
(Gợi ý: Thực hành viết lại bằng ngôn ngữ khác, hoặc dùng AI gen ra nhiều ngôn ngữ để tham khảo.)
Version 1: Chưa cải tiến — Vi phạm OCP & DIP
Sử dụng kế thừa (Inheritance) + ràng buộc chặt (Tight coupling).
// Base class
class PaymentProcessor {
process(amount) {
console.log(`Processing generic payment of $${amount}`);
}
}
// Subclasses kế thừa PaymentProcessor
class CreditCardPayment extends PaymentProcessor {
process(amount) {
console.log(`Processing credit card payment of $${amount}`);
}
}
class PayPalPayment extends PaymentProcessor {
process(amount) {
console.log(`Processing PayPal payment of $${amount}`);
}
}
// Order class phụ thuộc trực tiếp vào loại thanh toán cụ thể
class Order {
constructor() {
this.paymentProcessor = new CreditCardPayment(); // ❌ Hardcoded
}
checkout(amount) {
this.paymentProcessor.process(amount);
}
}
const order = new Order();
order.checkout(100);
Phân tích vấn đề:
Vấn đề | Mô tả |
---|---|
❌ Vi phạm OCP | Nếu thêm CryptoPayment , phải sửa code trong Order . |
❌ Vi phạm DIP | Order (module cấp cao) phụ thuộc trực tiếp vào CreditCardPayment (module cấp thấp). |
❌ Coupling cao | Không thể thay đổi loại payment khi runtime. |
⚠️ Dễ "Fragile Base Class Problem" | Nếu PaymentProcessor thay đổi logic, tất cả subclass có thể hỏng. |
Version 2: Cải tiến — Composition + Dependency Injection (chuẩn SOLID)
// Interface-like contract (ngầm định trong JS)
class PaymentMethod {
process(amount) {
throw new Error("process() must be implemented");
}
}
// Implementations (Concrete classes)
class CreditCardPayment extends PaymentMethod {
process(amount) {
console.log(`Processing credit card payment of $${amount}`);
}
}
class PayPalPayment extends PaymentMethod {
process(amount) {
console.log(`Processing PayPal payment of $${amount}`);
}
}
class CryptoPayment extends PaymentMethod {
process(amount) {
console.log(`Processing crypto payment of $${amount}`);
}
}
// High-level Order class now uses Composition + DI
class Order {
constructor(paymentMethod) {
this.paymentMethod = paymentMethod; // Inject dependency
}
checkout(amount) {
this.paymentMethod.process(amount);
}
}
// ---- Usage ----
const order1 = new Order(new CreditCardPayment());
const order2 = new Order(new PayPalPayment());
const order3 = new Order(new CryptoPayment());
order1.checkout(100);
order2.checkout(200);
order3.checkout(300);
Phân tích cải tiến theo SOLID
Nguyên tắc | Trạng thái | Giải thích |
---|---|---|
S — Single Responsibility | ✅ | Mỗi lớp làm đúng một việc (CreditCardPayment chỉ lo thanh toán thẻ, Order chỉ lo đặt hàng). |
O — Open/Closed | ✅ | Có thể thêm loại thanh toán mới mà không sửa code Order . |
L — Liskov Substitution | ✅ | Bất kỳ PaymentMethod nào đều thay thế được mà không phá vỡ logic. |
I — Interface Segregation | ✅ | PaymentMethod chỉ yêu cầu 1 hành vi process() . |
D — Dependency Inversion | ✅ | Order phụ thuộc vào abstraction (PaymentMethod ), không phụ thuộc vào lớp cụ thể. |
Kết luận
So sánh | Version 1 (Chưa tốt) | Version 2 (Chuẩn SOLID) |
---|---|---|
Cách thiết kế | Inheritance + Tight Coupling | Composition + Dependency Injection |
Mở rộng hệ thống | Phải sửa code cũ | Chỉ cần thêm class mới |
Tính linh hoạt | Thấp | Cao |
Phụ thuộc | Cụ thể | Trừu tượng |
Bảo trì | Khó | Dễ |
Tuân thủ SOLID | ❌ | ✅ |
Tóm tắt ý nghĩa thực tế:
- Composition over Inheritance giúp code dễ mở rộng và dễ test hơn.
- Khi thêm loại Payment mới → chỉ cần thêm class mới, không chạm vào hệ thống cũ.
- Tránh rủi ro “Fragile Base Class Problem” (base class thay đổi kéo theo lỗi hàng loạt).
All rights reserved