SOLID Principal

September 8, 2025


L – Liskov Substitution Principle (Nguyên tắc thay thế Liskov)

Một class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình.

  • Nếu B kế thừa A, thì các đối tượng B có thể sử dụng thay cho A mà không gặp lỗi.

Wrong

class Bird {
    public function fly() {}
}

class Ostrich extends Bird {
    public function fly() {
        throw new Exception("Đà điểu không biết bay!");
    }
}

Right

interface Bird { public function eat(); }
interface Flyable { public function fly(); }

class Sparrow implements Bird, Flyable {
    public function eat() {}
    public function fly() {}
}

class Ostrich implements Bird {
    public function eat() {}
}
  • Trọng tâm: Tính thay thế hợp lệ trong kế thừa.
  • Ý nghĩa: Nếu B kế thừa A, thì mọi nơi dùng A đều có thể thay bằng B mà không làm thay đổi logic chương trình.
  • Khi vi phạm: Thường xảy ra khi class con không thực sự là một loại của class cha.

Lợi ích của LSP

  1. Tính thay thế an toàn: Code hoạt động ổn định khi thay BaseClass bằng Subclass.
  2. Giúp thiết kế abstraction đúng đắn hơn: Buộc dev phải suy nghĩ “liệu quan hệ kế thừa này có thực sự đúng?”
  3. Dễ mở rộng mà không phá vỡ logic: Có thể thêm nhiều class con mà không làm hỏng phần code đang chạy.
  4. Giúp tái sử dụng: Khi abstraction đúng, hệ thống dễ tái sử dụng hơn (ví dụ Flyable áp dụng cho Chim, Máy bay, Drone…).

Bất cập của LSP

  1. Khó xác định “hợp lệ”: Trong thực tế, không phải lúc nào cũng rõ ràng subclass có thay thế hợp lệ không.
    • Ví dụ: Square kế thừa Rectangle nghe hợp lý về hình học, nhưng trong code lại vi phạm (khi thay đổi chiều dài/rộng độc lập).
  2. Thiết kế abstraction quá phức tạp: Để đảm bảo tuân thủ LSP, đôi khi ta phải tạo thêm nhiều interface/abstraction phụ, khiến hệ thống “rườm rà”.
  3. Tốn effort ban đầu: Cần nhiều phân tích trước khi viết code để tránh quan hệ kế thừa sai.
  4. Không phải lúc nào cũng khả thi: Với dự án nhỏ, việc “bẻ” abstraction để đúng LSP có thể làm chậm tiến độ.
Pages: 1 2 3 4 5 6