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

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

Dẫn nhập

alt text

Trong phần một, 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 decomposition thay thế cho generalization.

Lạm dụng generalization sẽ làm cho phần mềm trở nên phức tạp, khó maintain.

Việc thay thế nó bằng decomposition trong một số tình huống giúp code trở nên flexible, maintainable, resuable

Bắt đầu từ hành tinh kỳ lạ

Tít tận trong dải ngân hà xa xăm, có một ngôi sao vô cùng kỳ lạ mang tên Hardwarellelius. Nhiệt độ quanh năm luôn ở mức 1000 độ C. Không không khí, không nước, không sự sống. Ấy thế mà trên hành tinh lại có một chàng trai nửa người nửa sói mang tên LuluAP, một mình cai trị ngôi sao nhỏ suốt mấy nghìn năm. Hắn yêu thích sự cô đơn, tự kỉ trên cái hành tinh chết chóc ấy. Hắn yêu cái nóng đến rát da rát thịt. Hắn vốn là một kẻ lạ lùng đến từ một ngôi sao lạ lùng.

Cơ mà cũng có một ngày, chàng người sói ấy hứng thú lên đi ngao du thiên hà. Hắn lượn qua khắp các hành tinh trong vũ trụ, rồi lạc vào Trái Đất xinh đẹp lúc nào không biết. Hắn chưa kịp hết bất ngờ bởi sự thơ mộng, xanh tươi, yên bình của quả địa cầu - cái mà hắn chưa từng thấy trước đây - mà còn nhanh chóng bị đắm chìm bởi nét đẹp của những cô nàng thiếu nữ trên Trái Đất. Hắn xao xuyến biết bao ngày và không hề muốn quay lại ngôi sao của mình.

Thế rồi, hắn quyết định tiếp cận, tán tỉnh một cô gái mà hắn đã để ý từ lâu. Để làm điều đó, Lulu phải hoá thành con người. Và theo quy tắc của Đấng tạo ra Hardwarellelius, nếu Lulu hoá thành người trái đất, hắn sẽ được hoá thành một anh chàng thật thà, đẹp trai, tài giỏi, nhưng phải làm lập trình viên và bắt đầu từ mức kinh nghiệm là không có gì. Trình độ như một sinh viên năm 3 đại học, còn phải học tập và cố gắng nhiều. Tiền bạc không có nhiều, mỗi tháng chỉ được trợ cấp 5 triệu đồng. Không giàu có và phải tự vươn lên. Phải làm người trong vòng 5 năm rồi mới được phép trở lại thành người sói, quay về hành tinh của mình. Vì tình yêu dành cho cô con gái xinh đẹp kia, hắn quyết định làm theo. Tuy nhiên, xui thay, hắn đã va vào một cô gái ham tiền, hám của, thèm xe hơi, nghiện chơi bời. Và vốn không tiền trong tay, hắn nhanh chóng bị cô gái cho ra rìa và nhìn một thằng khác - không giỏi, không bảnh bao, không tử tế nhưng nhiều tiền - hốt mất. Hắn đau đớn lắm, hắn căm hận lắm, chưa bao giờ trái tim hắn biết đau đến vậy.

Ồ, các bạn biết không, điểm yếu của hắn chính là ở trái tim pha lê, dễ tổn thương ấy. Hắn có thể thổi lửa, tàng hình, bay lượn, dùng một tay đấm vỡ xe tăng, vô địch thiên hạ. Nhưng cũng như gót chân của Achilles, ta có trái tim của LuluAP...

Mỗi lần trái tim ấy đau đớn, trên ngôi sao Hardwarellelius, một Linh Hồn sẽ được sinh ra. Mỗi lần trái tim ấy căm hận, một viên Đá Quý sẽ mọc lên. Cô gái nào làm cho Lulu đau khổ, một hồn ma mang tên cô gái đó cũng sẽ được tạo thành. Nếu Đá Quý kết hợp cùng Linh Hồn, sẽ tạo nên Tinh Hoa - một loại đá có sức mạnh khủng khiếp, và nếu nó rơi vào tay bất kì con ma nào, con ma đó sẽ có một sức mạnh phi thường, và nó sẽ tìm cách tiêu diệt LuluAP, để chiếm lĩnh Hardwarellelius. Lúc ấy, chàng người sói phải có một người vợ bên mình, mới tiếp thêm năng lượng cho hắn, hắn mới đủ sức chống chọi với sức mạnh điên rồ đến từ những kẻ xấu xa kia.

Mỗi viên Đá Quý có các thuộc tính màu sắc (color), nhánh (branch) và tuổi đời (age). Khi ở bên cạnh chủ nhân, nó thể toả sáng (shine) cũng như kiên cường (resilience), đồng hành cùng chủ nhân cho tới hơi thở cuối cùng. Mỗi Linh Hồn có thuộc tính hệ (base) (ví dụ hệ nước, hệ lửa, hệ đất...) và ngôi sao chiếu mệnh (guardianStar). Nó có thể bay (fly) hay tàng hình (invisible). Nếu kết hợp thành Tinh Hoa, ngoài việc Tinh Hoa có những khả năng mà hai thứ kia có, nó còn có thể ngoại cảm (psychic), dự đoán những điều có thể sắp xảy ra đối với chủ nhân, dẫn lối (guide) - chỉ đường cho chủ nhân hay chưởng nội công (power) để bảo vệ chủ nhân.

Sau khi thất bại ở mối tình đầu tiên, Lulu tiếp tục hành trình tìm kiếm người vợ tương lai của mình. Nhưng hết người này đến người khác, tất cả đều từ chối tình yêu ấy. Là một lập trình viên chăm chỉ, hắn vùi đầu vô máy tính ngày đêm, vì một tương lai tươi sáng cho vợ con của mình. Nhưng vô tình điều đó lại khiến hắn "gà" đi trong việc tán gái. Từ giao tiếp, ăn nói cho đến biểu cảm, nhan sắc. Không khó để tìm ra lí do để những cô gái kia từ chối tình cảm mà Lulu dành cho. Đã vậy, hắn còn không giàu. Bao nhiêu điểm yếu đó thì lấy đâu ra cơ hội mà quen được một cô gái Trái Đất hiện đại trong cuộc sống ngày nay. Chắc hắn chỉ còn cách chờ ở tương lai mà thôi. Hắn mong những cố gắng của mình sẽ được đền đáp.

Rồi điều gì đến cũng đã đến, liên tiếp những lần đau khổ khiến những viên Đá Quý, Linh Hồn và những hồn ma mọc lên trên hành tinh quê hương của anh chàng người sói. Tình thế của ngôi sao Hardwarellelius đang ngày càng nguy cấp. Để cứu vãn nó, LuluAP chỉ còn cách học thật chăm chỉ, để sau này giàu có, rồi cưới một người vợ xứng đáng, cùng nhau quay về chống chọi với lũ ma xấu xa.

Nhưng trước tiên, Lulu phải mô hình hoá được những thứ sức mạnh đang tồn tại trên hành tinh của mình. Đó là Tinh Hoa, Đá QuýLinh Hồn. Việc mô hình hoá tối ưu sẽ giúp hắn có cái nhìn tổng quan hơn về những gì đang diễn ra, và từ đó có thể bắt đầu tính kế hoạch đối phó với những kẻ độc ác đó.

Một cuộc chiến không khoan nhượng!

Cách hiện thực dùng kế thừa

Ở tình huống trên, nhiều người sẽ hiện thực như sau

  • Tạo một lớp Đá Quý. Có các phương thức, thuộc tính mà Đá Quý vốn có
  • Tạo lớp Tinh Hoa kế thừa lớp Đá Quý, có thêm các phương thức, thuộc tính alt text

Thiết kế này không tồi, không vi phạm Liskov Substitution Principle, nhưng...

Nó không REUSABLE :scream: :scream:

Nó không tuân theo nguyên tắc SEPARATE OF CONCERN trong thiết kế, các method của Đá QuýLinh Hồn không liên quan, nhưng lại nằm chung trong một class :scream: :scream:

Lớp Tinh Hoa bị LOW COHESION, nó có nhiều vai trò :scream:

Vì sao không REUSABLE??

Giả sử có một class khác sau này có nhu cầu sử dụng lớp Linh hồn, ví dụ lớp Ngọc Bảo chẳng hạn, hay ta có nhu cầu cung cấp (export) class Linh Hồn như là một thư viện? Khi đó bạn phải implement lớp này từ đầu, sửa lại lớp Tinh Hoa cho consistent

Khi ta muốn thay đổi phần Đá Quý hay Linh Hồn trong một đối tượng Tinh Hoa, việc thay đổi sẽ dài dòng, luộm thuộm, dễ lỗi vì phải sửa từng field

Nếu có 2 tinh hoa cùng có chung một loại Đá Quý hay Linh Hồn, ta không chia sẻ được, mà phải hiện thực riêng cho từng đối tượng

Một cách thiết kế tốt hơn dùng Decomposition

Ta sẽ tạo các class như hình bên dưới (chú ý hình bên dưới hiện thực không đầy đủ)
alt text

Có hai class riêng Đá Quý, Linh Hồn, thể hiện tính Separate of Concern. Và việc tách riêng như vậy thì một class khác trong tương lai có thể sử dụng hai lớp này.

Class Tinh Hoa sẽ có 2 field gemstone, soul tương ứng với các class Đá Quý và Linh Hồn.

Khi tạo object Tinh Hoa, chỉ cần truyền vào constructor hai object Đá QuýLinh Hồn cho Tinh Hoa đó.

Các method của Tinh Hoa có thể dùng các method của hai lớp kia trong khi implement.

Làm như vậy thì ta có thể chia sẻ object Đá Quý, Linh Hồn giữa những object Tinh Hoa, không phải tạo riêng, dài dòng cho từng object, tăng tính reusable

Ngoài ra, việc thay đổi từng phần Đá Quý hay Linh Hồn trong Tinh Hoa cũng dễ dàng, ta chỉ cần thay đổi một field thôi, hoặc tạo đối tượng mới dễ dàng.

Ví dụ Tinh Hoa Lửa bao gồm Đá Quý Đỏ và Linh Hồn Lửa, sau một thời gian rơi vào tay Tâm Huệ Ma Pháp, hắn đã tách Linh Hồn Dơi ra khỏi Đá Quý Đỏ và kết hợp Đá Quý Đỏ cùng Linh Hồn Gió, tạo ra một thứ sức mạnh đe doạ cả nhân loại.

Các lớp Đá QuýLinh Hồn có thể thoải mái thay đổi implement cho các method mà không lo bị crash, miễn là signature của các method vẫn giữ nguyên

Tuy việc hiện thực như trên sẽ khiến cho class Tinh Hoa bị high coupling nhưng với nhiều ưu điểm kể trên, cách hiện thực này vẫn tốt hơn nhiều so với cách dùng kế thừa!! :smile:

Kết luận

Qua bài viết trên, mình đã giới thiệu một tình huống phổ biến mà các dev hay dùng kế thừa, tuy nhiên, dùng cách này làm cho code khó maintain về sau. Và khi đó, decomposition giúp ta giải quyết tốt hơn cả.

bkuhardware 30-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.
2 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
4 0
Dẫn nhập (Ảnh) 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àn...
Nguyễn Trọng Luân viết 10 tháng trước
4 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 1 năm trước
2 0
Bài viết liên quan
White
4 0
Dẫn nhập (Ảnh) 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àn...
Nguyễn Trọng Luân viết 10 tháng trước
4 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

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


White
{{userFollowed ? 'Following' : 'Follow'}}
3 bài viết.
2 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á!