Tránh lạm dụng Generalization khi thiết kế phần mềm
software architecture
4
software design
4
generalization
2
White

Nguyễn Trọng Luân viết ngày 29/11/2019

Dẫn nhập

alt text
Thiết kế phần mềm là một công việc hết sức quan trọng, nhất là đối với những phần mềm lớn, mang tính chiến lược lâu dài.
Thà ta dành nhiều thời gian để tạo một kiến trúc phần mềm tốt, flexible, maintainable - đồng nghĩa với khởi đầu chậm - còn hơn là lao đầu vào code ngay và sau này khi tai hoạ ập đến, phần mềm cần phải cải thiện này kia, ta phải đập lại mọi thứ từ đầu, rất mất thời gian. :astonished:

Khi thiết kế phần mềm, có 4 nguyên lý quan trọng mà ta cần nắm, triển khai theo: abstraction, ecapsulation, decomposition, generalization
Đa số các nguyên lí đều dễ vận dụng ngoại trừ generalization
Lạm dụng generalization không những không giúp hệ thống flexible, reusable, maintainable mà còn tạo ra thêm muôn vàn khó khăn.

Sau đây mình xin giới thiệu 2 lỗi thường gặp khi áp dụng kế thừa

Superclass là đủ rồi ông giáo ạ :dizzy_face:

Khi bạn thực hiện việc kế thừa, bạn phải tự hỏi rằng, liệu lớp con vừa tạo ra có gì khác so với lớp cha? Có thêm phương thức gì không? Có override phương thức nào không? Hay đơn giản chỉ là kế thừa để chia sẻ attributes, methods?

Nếu câu trả lời là chỉ để sẻ chia, thì bạn đang lạm dụng kế thừa

Xem ví dụ sau:
Lưu ý: Ví dụ bên dưới dùng cặp Người yêu cũ - người yêu cụ thể, có thể khiến một số bạn cảm thấy không thuyết phục. Nếu vậy, các bạn có thể dùng cặp Hủ tiếu - loại hủ tiếu, Pizza - loại Pizza cho dễ hình dung.

Giả sử bạn đang đang được yêu cầu xây dựng một phần mềm có sự xuất hiện của những object người yêu cũ của A.
Bạn đã tạo class ExGirlFriend hoàn hảo như hình bên dưới, có các method như smile, cry, sayLove
alt text
Bạn biết người yêu cũ của A và bạn muốn xây dựng luôn class cho từng người yêu cũ, trong constructor mỗi class, bạn dùng hàm setName của superclass

Giả sử những có người yêu cũ tên là Thuý Nguyễn
Bạn xây dựng class ThuyNguyen và trong constructor của nó, bạn viết như sau
alt text
Ý tưởng không tồi, bạn muốn tạo blueprint cho từng loại người yêu, nhưng bạn thấy không, subclass chỉ khác superclass mỗi constructor và thậm chí nó còn dùng method của superclass chứ k phải method riêng trong constructor.

Việc dùng superclass trong tình huống này là đủ, không cần phải tạo thêm subclass

Vi phạm nguyên lí thay thế Liskov

Nguyên lí này phát biểu như sau: "Lớp con thay thế được cho lớp cha khi và chỉ khi nó không làm thay đổi chức năng của lớp cha"
Thay đổi chức năng ở đây tức là không còn giữ bản chất mà lớp cha có khi kế thừa

Mình sẽ đưa ra 3 ví dụ

Ví dụ thứ nhất:
Giả sử ta có lớp Animals là một lớp trừu tượng với các method run(), speak(), walk()

  • Các lớp Dogs, Cat kế thừa lớp Animals được vì Dog, Cat về mặt thực tế có thể chạy, kêu, đi bộ
  • Lớp Wheal (Cá voi) thì không, đúng là cá voi cũng là Animals, nhưng nếu bạn kế thừa lớp Animals, các methods run(), walk() sẽ hiện thực ra sao? Đúng với thực tế? Bạn sẽ kế thừa và để im hoặc override bằng 1 method không làm gì cả?
  • Thậm chí Cá voi còn có thể swim(), bạn tạo thêm method swim() cho lớp Wheal và trong run(), walk(), bạn gọi swim()?

Ngôn ngữ sẽ không bao giờ báo lỗi bạn những điều này, nhưng bạn sẽ lãnh hậu quả về sau nếu làm nó. Code sau này sẽ rất khó hiểu :stuck_out_tongue_closed_eyes:

Ví dụ thứ hai:
Cũng tương tự ví dụ thứ nhất nhưng mang tính tự kỉ nhiều hơn
Giả sử ta có lớp MyLove là một lớp trừu tượng, mô tả tình yêu của tôi với các method tenderness(), lovely(), haveBrain(), dontLoveMoney()

  • Tâm Huệ hội tụ cả 4 yếu tố trên, nên kế thừa là hợp lý, tuy lớp này có method swear()
  • Duyên Ánh hội tụ cả 4 yếu tố, kế thừa được
  • Khánh Ngọc mê tiền, không nên kế thừa vì phải override method dontLoveMoney() thế nào?

Thôi, không chém gió nữa, sang ví dụ thứ 3
Giả sử bạn có lớp Vector có các method thêm phần tử vào cuối, chèn vào một vị trí, xoá phần tử ở một vị trí, truy xuất tại một vị trí, xoá phần tử ở cuối, chèn phần tử ở đầu,...
Bạn hiện thực lớp Stack, một lớp đáng lẽ ra chỉ nên có top(), pop(), push(), đem kế thừa lớp Vector
Mọi thứ ổn áp, Stack có các method cần có.
Tuy nhiên, dư các method kia.
Tuy bạn chơi đẹp, tự ý, không dùng các method khác, chỉ dùng 3 method trên, nhưng nó vẫn là một practice xấu về kế thừa, và vi phạm nguyên lí Liskov :smile:

Kết luận

Qua bài viết trên, mình đã giới thiệu hai lỗi thường gặp khi kế thừa
Việc dùng kế thừa đúng cách sẽ biến nó trở thành một công cụ mạnh mẽ, ngược lại, nó chỉ khiến ta thêm đau khổ về sau

bkuhardware - 29/11/2019

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Nguyễn Trọng Luân

3 bài viết.
1 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
2 0
Dẫn nhập (Ảnh) Trong (Link), mình đã giới thiệu hai lỗi thường gặp khi dùng generalization. Trong phần hai này, mình sẽ giới thiệu một cách dùng...
Nguyễn Trọng Luân viết 11 ngày trước
2 0
White
2 0
Dẫn nhập Khi bắt tay vào làm một dự án React, chắc hẳn các bạn beginner sẽ nghĩ tới ngay boilerplate createreactapp thần thánh. Và đi kèm với anh ...
Nguyễn Trọng Luân viết 3 tháng trước
2 0
Bài viết liên quan
White
42 0
Feature Toggle (hay Feature Flags) và Feature Rollout là một kĩ thuật phổ biến giúp bạn có thể quản lý được hành vi của phần mềm của mình mà không ...
Huy Nguyen viết gần 2 năm trước
42 0
White
2 0
Dẫn nhập (Ảnh) Trong (Link), mình đã giới thiệu hai lỗi thường gặp khi dùng generalization. Trong phần hai này, mình sẽ giới thiệu một cách dùng...
Nguyễn Trọng Luân viết 11 ngày trước
2 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
3 bài viết.
1 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!