SOLID – Bài 3: Liskov’s Substitution Principle (LSP)

List các bài đã viết:

Ta tiếp tục nguyên tắc thứ 3, ứng với chữ cái thứ 3 của SOLID – Liskov’s Substitution Principle(LSP). Tôi tạm dịch là “Nguyên Tắc Đại Diện Cha-Con Liskov”, nghe hơi kỳ dị và lệch nghĩa, nhưng vì tôi nhận thấy các bạn của tôi luôn gặp vấn đề về ghi nhớ nguyên tắc này nếu để nguyên mẫu. Vì thế tôi đã phải ngồi suy nghĩ cả nửa ngày để nghĩ ra một cái tên sao cho hình tượng nhất. Điều nữa là, tôi vẫn để lại chữ Liskov vì muốn giữ lại bản quyền tác giả, người đã đưa ra nó.

Bây giờ bạn sẽ tìm hiểu tại sao cái tên tôi đặt lại tương ứng với nguyên tắc này. Nguyên văn của nguyên tắc này phát biểu như sau:

“Lớp con phải luôn có thể thay thế hoàn toàn bằng lớp cha của nó.”

Có thể là tôi hiểu không tới, nhưng thành thực mà nói tôi không ưng ý với phát biểu này mấy. Vì vậy, tôi xin phép dịch lại theo ý mình cho dễ hiểu và dễ nhớ như sau, dĩ nhiên bạn có quyền thích kiểu nào cũng được miễn bạn cảm thấy thoải mái và không hiểu sai lệch là được:

“Lớp con khi đại diện cho lớp cha không được thay thế hành vi của của lớp cha”

Đây có thể hiểu một cách nôm na thế này: Những đứa con được quyền tự quyết định hành vi của nó miễn là không ảnh hưởng với truyền thống gia đình. Mỗi khi quyết định tạo ra một lớp thừa kế từ một lớp khác, bạn sẽ được thừa kế một số phương thức được cài đặt sẵn từ lớp cha. Bạn không nên điều chỉnh hành vi của các phương thức này tại các lớp con. Bạn chỉ nên cài đặt một số phương thức khác được đánh dấu là trừu tượng (abstract) trên lớp cha.

Vì sao?

  • Trước tiên, bạn phải tôn trọng ý tưởng của người đi trước. Khi họ tạo ra 1 class, họ đã có một kế hoạc cụ thể chỉ ra phương thức nào được kế thừa không nên sửa, phương thức nào được quyền sửa (virtual), phương thức nào nên được cài đặt ở lớp con (abstract). Bạn nên làm theo.
  • Ngược lại nếu không định nghĩa rõ điều này trong thiết kế, rất có thể người tạo ra lớp kế thừa sẽ vô tình thay đổi hành vi của cả chương trình mà không hề biết.

Xin xem ví dụ sau đây: Bạn tạo ra 1 lớp Dice (xúc sắc 6 mặt) như sau:

Người khác muốn tạo ra 1 lớp mới DiceEight (xúc sắc 8 mặt) thừa kế lại Dice để dùng lại property Rnd cho tiện. Nhưng vì xúc sắc mới 8 mặt nên anh ta sửa lại ThrowAValue cho phù hợp như sau:

Tuy nhiên, một tester viết phương thức test class dice của bạn, anh ta không hề biết rằng sẽ có người sẽ sửa vào phương thức ThrowAValue:

Rõ ràng nếu một ai đó dùng phương thức TestDice cho một object thuộc kiểu DiceEight anh ta sẽ gặp lỗi:

Nguyên tắc này thực chất làm cụ thể hơn nguyên tắc 2 Open Close Principle để ràng buộc các lớp con kế thừa lại từ lớp cha nhưng ko sửa đổi hành vi lớp cha. Như đã nói, nguyên tắc khác cũng đều xoay quanh nguyên tắc 2.

Enjoy coding!

No Comments

Post A Comment