← SOLID Principles

Bài 5 · Nâng cao · 18 phút· Cập nhật 11/06/2026

I - Interface Segregation

Biên soạn bởi Nguyễn Anh Tuấn

Interface Segregation Principle (ISP): ưu tiên tạo ra các interface nhỏ, chuyên biệt thay vì một interface lớn, để các lớp chỉ cần triển khai những gì chúng thực sự cần

Interface Segregation Principle (ISP): đừng ép một lớp phụ thuộc vào những method nó không dùng. Thà nhiều interface nhỏ, chuyên biệt, còn hơn một interface to ôm đồm. Xem một interface "béo":

❌ Vi phạm ISP - Robot bị ép ăn & ngủ

interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
}

class Person implements Worker {
  work()  {}
  eat()   {}
  sleep() {}
}

class Robot implements Worker {
  work()  {}
  // Robot khong an, khong ngu - nhung van bi ep implement:
  eat()   { throw new Error("Robot khong an!"); } // hoac de trong (no-op)
  sleep() { throw new Error("Robot khong ngu!"); }
}
  • Robot buộc implement eat()/sleep() dù không bao giờ dùng.
  • Để trống (no-op) = nói dối kiểu; ném lỗi = bom nổ chậm lúc chạy.
  • Người gọi tin "mọi Worker đều eat()" và có thể gọi nhầm.

Chẻ interface béo thành các interface nhỏ; mỗi lớp implement đúng cái nó cần:

✅ Tuân thủ ISP - nhiều interface nhỏ

interface Workable { work(): void; }
interface Eatable  { eat(): void; }
interface Sleepable { sleep(): void; }

class Person implements Workable, Eatable, Sleepable {
  work()  {}
  eat()   {}
  sleep() {}
}

class Robot implements Workable {   // chi nhung gi Robot thuc su lam
  work() {}
}
  • Robot chỉ implement Workable - không còn method thừa.
  • Person ghép nhiều interface khi nó thực sự có các khả năng đó.
  • Hợp đồng giờ phản ánh ĐÚNG khả năng - không lừa người gọi.

Lợi ích lớn nhất: hàm/lớp chỉ khai báo phụ thuộc tối thiểu, nhờ vậy linh hoạt và dễ test:

Khai báo đúng khả năng tối thiểu

// Ca lam chi can Workable - nhan duoc ca Person lan Robot
function vanHanhCa(workers: Workable[]) {
  workers.forEach((w) => w.work());
}

// Khi can nhieu kha nang, ghep kieu lai:
function nghiTrua(x: Eatable & Sleepable) {
  x.eat();
  x.sleep();
}
  • Client phụ thuộc interface nhỏ → ít lý do phải đổi theo, ít coupling.
  • Mock trong test chỉ cần implement đúng method cần → test gọn.
  • TypeScript cho ghép kiểu (A & B) khi cần nhiều khả năng cùng lúc.
  • Dấu hiệu vi phạm: lớp implement method rỗng hoặc "throw not supported".
  • ISP và LSP đi cùng: interface vừa khít giúp lớp con luôn giữ trọn hợp đồng.
  • Đừng chẻ vụn vô nghĩa - gom các method THƯỜNG ĐI CÙNG NHAU vào một interface gắn kết.

ISP là SRP cho interface

Nếu SRP bảo "mỗi lớp một trách nhiệm", thì ISP bảo "mỗi interface một nhóm khả năng gắn kết". Cả hai cùng nhắm tới việc giảm phụ thuộc thừa - để mỗi phần thay đổi độc lập.

Câu hỏi thường gặp

SRP nói về LỚP (một lớp một trách nhiệm - nhìn từ phía người viết lớp). ISP nói về INTERFACE/hợp đồng (đừng bắt client phụ thuộc thứ nó không cần - nhìn từ phía người DÙNG interface). Một interface béo thường ép cả lớp implement gánh thêm trách nhiệm thừa, nên hai nguyên tắc hay đi cùng nhau.

Vì nó nói dối: kiểu (type) bảo "Robot biết eat()", nhưng thực tế không. Người gọi tin vào hợp đồng và có thể gọi nhầm, gây bug âm thầm hoặc phải ném lỗi lúc chạy. Đây cũng là cửa ngõ vi phạm LSP (lớp con không giữ được hợp đồng).

Bài L - Liskov Substitution: giữ lời hứa của lớp cha →

Một lớp có thể implement nhiều interface cùng lúc (vd Person implements Workable, Eatable, Sleepable). Client chỉ khai báo phụ thuộc vào interface nó cần. TypeScript còn cho ghép kiểu (Workable & Eatable) khi cần "vừa làm vừa ăn".

Vừa đủ để không ai phải implement method thừa, nhưng đừng nhỏ tới mức mỗi interface một method vô nghĩa. Nhóm các method THƯỜNG ĐI CÙNG NHAU vào một interface gắn kết (cohesive). Để nhu cầu thực tế của client dẫn đường.

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/5 Điểm: 0

Interface Segregation Principle khuyên điều gì?

Bài tập về nhà

  1. 1

    Soi method thừa

    Lấy ví dụ interface Worker (work/eat/sleep). Với mỗi lớp (Person, Robot), đánh dấu method nào là thừa.

    ✅ Hoàn thành khi: Chỉ ra Robot bị ép eat()/sleep(); nêu hậu quả của việc để no-op hoặc ném lỗi.

  2. 2

    Tách interface

    Refactor Worker thành các interface nhỏ (Workable, Eatable, Sleepable) và cho Person/Robot implement đúng nhu cầu.

    ✅ Hoàn thành khi: Không lớp nào còn method thừa; Robot chỉ implement Workable.

  3. 3

    Tự tìm trong thực tế

    Tìm một interface "béo" trong thư viện/SDK em hay dùng (vd interface có cả đọc lẫn ghi mà client chỉ cần đọc). Đề xuất cách tách.

    ✅ Hoàn thành khi: Chỉ ra interface cụ thể; đề xuất ≥2 interface nhỏ hợp lý.

  4. 4

    Ghép khi cần

    Viết một hàm chỉ nhận đúng khả năng nó cần (vd function caLamCaAn(x: Workable & Eatable)).

    ✅ Hoàn thành khi: Tham số khai báo đúng khả năng tối thiểu; không yêu cầu thừa.