0

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.)


image

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí