Mỗi lần đọc source code, chắc hẳn các bạn đều phải đối mặt với những biểu thức logic phức tạp. Nhiều khi nó phức tạp tới mức làm ta tốn cả ngày tìm hiểu để biết xem biểu thức đó kiểm tra điều kiện gì. Ngay cả khi có comment đi kèm thì lúc ta bắt tay vào sửa logic cũng phải vô cùng đau đầu mới đảm bảo được source code sau khi đã sửa đổi đáp ứng được yêu cầu mới, nhưng vẫn đảm bảo chạy đúng với các yêu cầu spec trước đó.
Việc gặp phải một biểu thức logic phức tạp trong khi đang đọc flow của chương trình khiến cho quá trình đọc hiểu của chúng ta bỗng dưng bị tắc nghẽn lại. Là người Việt Nam sống ở đô thị lớn như Hà Nội hay Sài Gòn, ai chẳng trải nghiệm tắc đường, nhưng sự bế tắc mà lập trình viên gặp phải trong trường hợp deadline đang đuổi thì chắc còn mang lại sự tức giận, khó chịu hơn rất nhiều!.
Key
Với những biểu thức logic lớn và phức tạp, chúng ta cần chia nhỏ thành những phần đơn giản hơn
Nào chúng ta cùng đi hiện thực hoá key đó với 7 cách dưới đây
Cách đơn giản nhất để chia nhỏ biểu thức phức tạp là sử dụng biến mới để lưu giá trị trung gian. Biến này được gọi là Biến thuyết minh vì việc thêm biến số đó vào giúp việc đọc hiểu biểu thức logic được nhanh và người đọc cũng hiểu rõ ý đồ của người viết code hơn.
Chúng ta cùng tham khảo ví dụ dưới đây:
|
|
|
|
Mới nhìn qua khó mà biết sau if người ta đang muốn check cái gì?
Nếu sử dụng biến thuyết minh chúng ta sẽ có đoạn code mới như sau:
|
|
|
|
Đôi khi với những biểu thức không phức tạp, chúng ta cũng nên sử dụng thêm biến vào để việc nắm bắt và sửa code được nhanh hơn.2. Sử dụng biến tóm lược
Chúng ta hãy cùng xem xét ví dụ sau đây:
|
|
Dù đoạn check logic request.user.id == document.owner_id
không phức tạp, nhưng với 5 biến số trong đó như vậy người đọc vẫn phải định thần mất một chút mới hiểu được. Nếu chúng ta thêm một biến tóm lược vào thì sẽ nhanh hơn rất nhiều.
|
|
Biến user_owns_document
được khai báo ngay trên đầu nên càng giúp cho việc đọc hiểu code phía sau được nhanh hơn.
Định luật Demorgan
not (a or b or c) <=> (not a) and (not b) and (not c)
not (a and b and c) <=> (not a) or (not b) or (not c)
Các bạn có thể nhớ định luật này một cách đơn giản là nếu có not ở ngoài thì bên trong or/and sẽ đảo nhau.
Các bạn sẽ gặp nhiều bài toán mà nếu suy nghĩ theo hướng thông thường trong thực tế và viết biểu thức thì nhìn biểu thức đó sẽ rất phức tạp. Nhưng chỉ cần biến đổi đi một chút, trông biểu thức cũng đã gọn gàng hơn rất nhiều rồi.
Hãy xem ví dụ dưới đây:
|
|
Vận dụng định luật De Morgan ta có
|
|
Biểu thức nhìn đã sáng sủa và dễ đọc hơn nhiều.
Đọc lướt qua liệu các bạn có hiểu ngay ý nghĩa của biểu thức này không?
|
|
Còn nếu viết như thế này thì sao?
|
|
Thông thường, việc viết cả biểu thức khó hiểu trên một dòng thường là để thể hiện cái tôi cá nhân, hay ngầm thể hiện rằng là đầu óc mình thông minh nên mới nghĩ ra được như vậy. Cũng có trường hợp người viết thích thú với thử thách tạo ra sự khác biệt, nhưng vô tình lại làm khó người đọc hiểu code về sau.
Giả sử chúng ta đang implement một function trong struct Range dưới đây:
|
|
Đầu tiên chắc hẳn ta sẽ nghĩ tới case mà đầu của range này phải lớn hơn đầu của range kia, đồng thời phải nhỏ hơn đuôi của range ấy:
Nên ta sẽ có biểu thức
|
|
Hoặc là end của range này phải lớn hơn begin của range kia và nhỏ hơn end của rang ấy:
Ta lại có
|
|
Tương tự ta sẽ có hàm đầy đủ là:
|
|
Vâng, ở đây mình đã làm sẵn cho các bạn khâu đặt dấu = vào đâu cho hợp lý với < hay <=, > hay >= rồi. Vậy mà biểu thức nhìn vẫn khiến người ta hoa cả mắt.
Đã vậy ai sẽ đảm bảo rằng các bạn đã care hết tất cả các case và sẽ ko có case nào cần check bị lọt???
Khi thấy một biểu thức phức tạp như vậy, là lúc chúng ta nên thay đổi cách tiếp cận vấn đề của mình. Thay vì tìm điều kiện để 2 vùng giao nhau, ta thử đặt giả thiết ngược lại, tìm điều kiện để 2 vùng không giao nhau trước. Với điều kiện ấy ta chỉ có 2 trường hợp phải xét tới!
|
|
Ngắn gọn, không sai sót và đảm bảo care hết tất cả các case.
Hãy nhìn kĩ đoạn code sau:
|
|
Các bạn có thấy sự tương đồng không? Tương đồng nhưng trông rất rối mắt. Dạng tổng quát của các dòng lệnh là
add_to->set_XXX(add_from.XXX() + add_to->XXX());
Thay vì viết đi viết lại n lần như vậy, trong C++ ta có thể sử dụng Macro để làm source code được ngắn gọn hơn như sau:
|
|
Mặc dù có nhiều khuyến cáo không nên lạm dụng Macro, tuy nhiên trong trường hợp này việc sử dụng nó giúp code của bạn nhìn đơn giản và đẹp mắt hơn rất nhiều. Cái lợi mà nó mang lại lớn hơn cái bất lợi khi sử dụng nó rất nhiều.
Trên đây mình đã giới thiệu tới các bạn phần 3 của series Nghệ thuật code đẹp. Nghệ thuật viết code đẹp – Viết flow điều kiện và vòng lặp dễ hiểu
Mong rằng những chia sẻ này sẽ giúp code các bạn viết ngày một đẹp hơn.
Via Techtalk.vn