Tiếp theo chuỗi bài về SOLID principles, bài này chúng ta sẽ tìm hiểu về nguyên tắc phân tách Interface nhé
Interface Segregation Principle (ISP) là gì?
Nguyên tắc phân tách Interface khẳng định rằng không nên buộc các entities (như class, module...) phải phụ thuộc vào các phương thức (method) mà nó không sử dụng đến.
Hay giải thích bằng cách khác: Bạn không nên tạo ra interface lớn, mục đích chung chung bao gồm quá nhiều method, hãy ưu tiên tạo ra nhiều interface nhỏ, mục đích cụ thể. Theo đó các class chỉ cần implement các interface mà nó thực sự dùng đến.
Nguyên tắc này được giới thiệu bởi Robert C. Martin hay còn gọi là Uncle Bob
Nghe có vẻ khó hiểu phải không?
Chúng ta hãy làm rõ vấn đề qua các ví dụ nhé.
Ví dụ về sự vi phạm nguyên tắc phân tách interface (ISP)
interface Worker {
work(): void;
eat(): void;
sleep(): void;
}
class Person implements Worker {
eat() {
console.log('Person can eat')
}
work() {
console.log('Person can work')
}
sleep() {
console.log('Person can sleep')
}
}
class Robot implements Worker {
eat() {
// forced to eat => violate
console.log('Robot cannot eat')
}
work() {
console.log('Robot can work')
}
sleep() {
// forced to eat => violate
console.log('Robot cannot sleep')
}
}
Ở ví dụ này ta thấy class Person hoàn toàn có đủ các hành vi như work
eat
và sleep
nhưng điều này không hoàn toàn đúng với class Robot
Robot không eat
mà cũng không sleep
, việc ép buộc class Robot phải implement 02 method này đã vi phạm nguyên tắc phân tách interface.
Chúng ta cùng thiết kế lại ví dụ trên theo nguyên tắc phân tách interface nhé
Ví dụ tuân theo nguyên tắc phân tách interface
// Adhering
interface Eatable {
eat(): void;
}
interface Workable {
work(): void;
}
interface Sleepable {
sleep(): void;
}
class Person implements Eatable, Workable, Sleepable {
eat() {
console.log('Person can eat')
}
work() {
console.log('Person can work')
}
sleep() {
console.log('Person can sleep')
}
}
class Robot implements Workable {
work() {
console.log('Robot can work')
}
}
Ở ví dụ này chúng ta đã phân tách các hành vi ra th ành các interface riêng biệt.
Với class Person, hiển nhiên con người có thể eat
work
và sleep
nên chúng ta implement toàn bộ 3 interface Eatable
Workable
và Sleepable
Còn với class Robot, rotbot chỉ có thể work
nên chúng ta chỉ implement interface Workable
Điều này giúp làm giảm lượng code rác, không cần thiết và cũng khiến cho hệ thống linh hoạt hơn, implement vừa đủ, không thừa không thiếu
Unit test
Tiết mục unit test quen thuộc lại xuất hiện
describe("Person", () => {
it("able to eat", () => {
const person = new Person()
expect(person.eat()).not.toThrow()
})
it("able to work", () => {
const person = new Person()
expect(person.work()).not.toThrow()
})
it("able to sleep", () => {
const person = new Person()
expect(person.sleep()).not.toThrow()
})
})
describe("Robot", () => {
it("able to work", () => {
const robot = new Robot()
expect(robot.work()).not.toThrow()
})
})
Hỏi đáp
Q: Làm thế nào để có thể ứng dụng nguyên tắc phân tách interface trong code của bạn?
A: Để ứng dụng nguyên tắc phân tách interface, tôi làm theo các bước sau:
- Định nghĩa ra các interface nhỏ, tập chung vào yếu tố chuyên biệt, cụ thể của interface đó.
- Tránh tạo ra các interface lớn (gồm nhiều method) mà các class phải implements những method không cần dùng tới.
- Ưu tiên sử dụng nhiều interface chuyên biệt thay vì sử dụng một interface lớn hoặc interface mang tính chất chung chung.
Tổng kết
Nguyên tắc phân tách interface (ISP) giúp hạn chế code rác, tập chung vào sự chuyên biệt của từng interface, nâng cao nhận thức với từng hành vi.
Điều này rất tốt cho việc tổ chức code và bảo trì cũng như khả năng kiểm thử (test)
Nếu bạn phát hiện sai sót, đừng ngần ngại báo lại cho mình qua các kênh mạng xã hội bên dưới website.
Các bài viết tiếp nối chủ đề SOLID principles sẽ sớm được cập nhật.
Chúc bạn ngày mới tốt lành.