[Code sao cho chuẩn] Bài 3 – Bàn về comment code.

      Có nên viết comment code hay không có lẽ là một trong những vấn đề gây tranh cãi dai dẳng nhất trong giới lập trình.

     Thuở còn đi học, mình hay nghe thầy giáo dạy rằng nên comment code đầy đủ, và có lẽ nhiều người khác cũng nghĩ như vậy. Thế nhưng sau này khi đi làm, ta thường được nghe những câu nói kiểu như “viết code thế này thì comment làm gì cho rắc rối”, “comment gì mà như lừa người”, “code chuẩn là không cần comment” ..v.v. Hơn thế, còn có ý kiến cho rằng cảnh giới cao nhất của việc viết code là self-document, tức là không cần phải comment mà người khác vẫn có thể hiểu dễ dàng. Có vẻ mâu thuẫn đã bắt đầu xuất hiện rồi nhỉ? Chúng ta sẽ băn khoăn: chúng ta có thức sự cần comment code? Và cuối cùng thì quan điểm của ai là đúng đây?

comment code 4.jpg

        Theo mình thì, chúng ta vẫn nên comment code. Tuy nhiên cần làm rõ những điểm mà chúng ta nên comment và những nơi chúng ta không nên comment. Có như vậy chúng ta mới có một định hướng rõ ràng hơn và một sự đánh giá đúng đắn hơn cho việc comment code, cũng như tránh được những sự nhập nhằng và xung đột giữa các luồng quan điểm trái ngược nhau.

Những lúc không nên comment code

1. Comment code gây lãng phí thời gian vô ích

       Đầu tiên, chúng ta phải biết rằng, nếu có những dòng comment, người đọc sẽ mất thời gian để đọc và hiểu nó là gì, việc đọc comment lấy mất đi thời gian quý báu của người đọc. Bởi thế nếu việc comment code gây nên những lãng phí thời gian vô ích thì chúng ta nên xem xét lại sự hiện diện của chúng. Xét ví dụ sau đây:

// The class definition for Account
public class Account {
    //Constructor
    Account();

    // Set the profit member to a new value
    public void SetProfit(double profit);

    // Return the profit from this Account
    public double GetProfit();
};

          Đã từng có một khoảng thời gian, mình viết comment kiểu thế này, và thật sự là bây giờ khi đọc lại, mình có một cảm giác kiểu như “muốn đấm vào mặt”. Bởi vì sao? Bởi vì những comments này rất … vô ích.

          Những dòng comment ở trên không có một tác dụng nào khác ngoài việc lãng phí thời gian người đọc một cách vô ích. Những dòng code đã quá rõ ràng và có thể hiểu được ngay lập tức thì tốt nhất là đừng nên comment, chỉ nên comment những dòng code phức tạp kiểu thế này thì hợp lí hơn:

# remove everything after the second '*'
name = '*'.join(line.split('*')[:2]);

       Mặc dù chúng ta vẫn có thể hiểu được ý nghĩa của những dòng code trên, tuy nhiên sẽ nhanh hơn rất nhiều nếu chúng ta nhận ra ngay được ý tác giả qua dòng comment. Và như tiêu chí mình nói xuyên suốt series này: code nên được viết sao cho người khác dễ hiểu nhất, việc dùng một comment ở đây theo mình là hợp lí.

2. Tránh dùng comment để lấp liếm vấn đề

       Đôi khi, việc hạn chế comment cũng giúp người lập trình bớt lười biếng hơn. Khi đó, họ buộc phải viết code có ý nghĩa hơn là việc dùng comment để lấp liếm các dòng code không tốt. Chúng ta xét thử thêm một tình huống mà việc comment trở nên không tốt như sau:

// Releases the handle for this key
// doesn't modify the actual registry in database.
void DeleteRegistry(RegistryKey* key);

        Nếu như bạn nào từng lập trình qua C++ thì chắc sẽ biết tới khái niệm con trỏ, trong C++ không có cơ chế giải phóng bộ nhớ tự động như các ngôn ngữ lập trình cấp cao khác, khi không dùng nữa chúng ta cần phải tự delete con trỏ đi để không gây nên vùng nhớ rác.

       Ở đây, người lập trình đơn giản chỉ muốn giải phóng vùng nhớ mà con trỏ đang nắm giữ, thế nhưng cách đặt tên hàm thực sự có vấn đề: nó khiến người ta hiểu lầm chức năng của hàm này là xoá 1 Registry ở trong database đi. Để tránh hiểu lầm thì người viết comment một dòng ý nghĩa thực sự cho nó. Việc dùng comment để lấp liếm vấn đề có thể gây thảm hoạ nếu người kế thừa không thực sự hiểu rõ ý nghĩa của nó.

        Trong trường hợp này, tốt hơn là chúng ta không nên comment mà nên sửa lại code để bản thân nó đã dễ hiểu mà không cần đến comment để giải thích. Chúng ta sẽ sửa lại tên hàm như sau:

void ReleaseRegistryHandle(RegistryKey* key);

     Việc sửa lại tên hàm đã làm cho code trở nên sáng sủa hơn rất nhiều. Rõ ràng, bản thân việc đặt tên biến cũng là một vấn đề rất quan trọng trong lập trình, chúng ta nên cẩn thận.

       Ngoài ra, mọi người cũng nên chú ý đến việc “comment rác”, tức là ý nghĩa comment một đàng mà code xử lí một kiểu. Khi sửa chức năng của một đoạn code nào đó, hãy đảm bảo là những dòng comment cũng được kiểm tra lại tương ứng. Tóm lại, chúng ta nên nhớ những trường hợp không nên comment code sau:

- Không comment những đoạn code mà ý nghĩa của nó đã quá rõ ràng.
- Không comment lấp liếm để che giấu vấn đề, hãy giải quyết vấn đề triệt để và rõ ràng hơn.
- Xoá ngay lập tức những comment nếu đã xoá code đi kèm, tránh dư thừa comment.

Những lúc chúng ta cần comment code

      Mặc dù không phải lúc nào chúng ta cũng nên viết comment code, nhưng khi nào thì chúng ta cần comment code? Câu trả lời hợp lí là: comment phải mô tả được suy nghĩ của người viết code và giải đáp được những thắc mắc của người đọc.

       Comment không chỉ nên là việc giải thích đoạn code này làm gì, mà nó nên mang cả ý nghĩa “tại sao” người viết lại viết như vậy, thậm chí dự đoán trước được những điều mà người đọc sẽ có khả năng thắc mắc trong tương lai. Chúng ta xem xét thử những tình huống sau:

1. Cần nói rõ ý định tại sao

      Viết code để chương trình hoạt động đúng đã là 1 việc khó, việc làm cho người khác hiểu được ý định của mình đằng sau những đoạn code lại càng khó hơn. Cùng một vấn đề nhưng đôi khi có nhiều cách để giải quyết, chúng ta nên xem xét việc comment để nói rõ “tại sao” ta lại xử lí như vậy.

      Giả sử ta có 1 đoạn code chạy đa luồng với biến hằng NUMBER_OF_THREADS, giả sử ta đều có thể chạy với giá trị 2, 4 hoặc 8 luồng 1 lúc, tuy nhiên ta lại chọn 4 vì nó phù hợp với cấu hình đại đa số người dùng (không quá ngốn RAM chẳng hạn), khi đó chúng ta cũng nên comment để người đọc hiểu được tại sao ta chọn con số 4.


//Can run with value of 2, 4 or 8
// but 4 is best for performance
NUMBER_OF_THREADS = 4;

2. Code dễ bị hiểu sai

Thử xét một đoạn code C++ dùng để xoá dữ liệu:


public class Recorder {
   vector<float> data;
   ...
   public void Clear() {
      vector<float>().swap(data); // Huh? Why not just data.clear()?
   }
}

      Chắc chắn sẽ có người thắc mắc rằng tại sao không dùng lệnh clear() của vector mà lại dùng lệnh swap(), hay hẳn là người viết đã sai điều gì đó ở đây?

      Trong C++, thì vector chỉ thực sự được giải phóng vùng nhớ nếu chúng ta dùng lệnh swap(), nó là một đặc điểm của thư viện STL chuẩn mà không phải ai cũng biết. Để tránh hiểu lầm không đáng có, tốt nhất nên thêm vào một comment để người sau khỏi hoang mang:


// Force vector to relinquish its memory (look up "STL swap trick")
vector<float>().swap(data);

3. Những comment mang lại cái nhìn bao quát

     Nếu bạn tham gia một dự án nào đó, mà bạn không phải là người phát triển ngay từ đầu, điều khó nhất có thể không chỉ là những hiểu biết mang tính kĩ thuật, mà đến từ việc “hiểu được dự án đó đã và đang thực sự làm gì”.

      Với một dự án đã được phát triển một thời gian dài, thậm chí tìm được điểm bắt đầu (entry point) của project khi chạy là chỗ nào đã là cả một vấn đề khó khăn rồi. Bạn nghĩ sao nếu với cái đống source code hàng chục file, mỗi file lên tới cả ngàn dòng code, việc đọc code không hề có comment nó khủng khiếp cỡ nào!!!

     Giả sử, trong mô hình lập trình web, chúng ta thường dùng mô hình MVC gồm 3 lớp tiêu chuẩn: Models thao tác với dữ liệu, Controller điều khiển nghiệp vụ, View để hiển thị. Thế nhưng, thực tế mọi chuyện không hề đơn giản như thế.

      Chúng ta xây dựng thêm các lớp để cache dữ liệu nhằm giảm tải truy vấn xuống DB, chúng ta xây dựng các lớp logic mặt nạ (façade class) để gom nhóm các thao tác ở tầng xử lí thấp hơn, ..v.v.v. Những kĩ thuật nhỏ đó làm code trở nên phức tạp hơn rất nhiều so với mô hình được học, sẽ là tốt hơn nên chúng ta có những comment mang tính bao quát kiểu như:

//This file contains helper functions that provide a more
//convenient interface to process IP value.
//It handles accessing permissions and other nitty-gritty details.
class WBN_IP_Logic{

    //hundreds of functions here
    ...

    function isIPv4(){
    }

    function checkValidIP{
    }
}

       Hay kiểu như:

//This class looks complicated, but it’s really
//just a smart cache.
//It doesn’t know anything about the rest of the system
class WBN_Sessions{
    ...
    //thousands lines of code here
    ...
}

        Những dòng comment kiểu như thế này sẽ tiết kiệm cho người đọc rất rất nhiều thời gian so với việc đọc hàng ngàn dòng code để thực sự hiểu được chúng thực sự có ý nghĩa gì. Những dòng code này mang ý nghĩa như bản document mô tả lại ý nghĩa thực sự của những đoạn code mà người dùng đang đọc. Thiếu nó thì chắc hẳn người đọc cũng sẽ vẫn hiểu thôi, tuy nhiên chẳng phải là sẽ tốt hơn nếu người đọc có thể hiểu được nhanh hơn hay sao?

4. Những đoạn code thực sự khó hiểu

     Có ý kiến cho rằng: code tốt nhất là bản thân nó phải có tính self-document , tức là không cần phải comment và người khác vẫn hiểu. Điều này liệu có đúng? Phải chăng là những dòng code đó sẽ dễ đọc như văn viết?

       Mình nghĩ rằng, có những đoạn code bản chất đã khó hiểu, rất khó để làm nó trở nên dễ đọc hơn được.

      Ngoài những xử lí thông thường, đôi khi chúng ta có thứ gọi là “thuật toán” hoặc là “design pattern”. Những thứ đó đôi khi rất phức tạp, nếu code mà không có comment chú thích thì rất khó để người đọc hình dung đó là gì. Tốt hơn là ta cứ nên viết thêm dòng comment để nói ra đó là gì. Xét ví dụ sau, dò tìm “vùng blob” trong ảnh:

int find_blobs(const vec2Dc&amp; image)
{
    ...
    while (true) {
        struct Blob blob;
        ...
        while (index < blob.elements_number) {
            unsigned int N = (unsigned int)blob.elements_number;
            for (unsigned int i = index; i < N; i++) {
                add_up_neighbour(blob, i);
                add_right_neighbour(blob, i);
                add_down_neighbour(blob, i);
                add_left_neighbour(blob, i);
            }
            index = N;
        }
        remove_blob_from_image(blob);
        ...
    }
}

     Đoạn code trên chỉ là 1 phần nhỏ trong cả hàm hoàn chỉnh. Thực sự nếu không có gợi ý thì có lẽ tất cả – hoặc hầu hết – đều không hiểu nó làm gì. Có những đoạn code mà thực sự bản chất nó đã khó hiểu, vậy nên tốt nhất là ta nên thêm comment để nói rõ ý nghĩa của nó. Cố gắng viết code dễ hiểu là điều đáng hoan nghênh, nhưng nếu không thể, thì chí ít bạn hãy dùng comment để làm rõ ý nghĩa cho nó:

     ...
    //Find blob by comparing kernel
    while (index < blob.elements_number) {
        unsigned int N = (unsigned int)blob.elements_number;
        for (unsigned int i = index; i < N; i++) {...}
    }

Kết

      Tốt hơn cả, mọi người nên nghĩ rằng, việc comment code là để giúp cho người đọc biết được thực sự người viết đã viết gì, đã có suy nghĩ gì và đã giải quyết vấn đề gì:

  • Cần tránh những comment quá vô dụng hoặc lấp liếp:
    • Không comment những đoạn code mà ý nghĩa của nó đã quá rõ ràng.
    • Không comment lấp liếm để che giấu vấn đề, hãy giải quyết vấn đề triệt để và rõ ràng hơn.
    • Xoá ngay lập tức những comment nếu đã xoá code đi kèm, tránh dư thừa comment.
  • Nên comment khi thực sự nó giúp ích cho việc đọc và hiểu code dễ dàng hơn:
    • Comment cho trường hợp có một lí do nào đó đặc biệt (Why, How).
    • Comment cho trường hợp dễ bị hiểu sai
    • Comment miêu tả cho những cái nhìn bao quát.
    • Comment cho những điều thực sự khó hiểu.

     Chúc vui, enjoy coding!

Tham khảo:
Sách: Clean Code: A Handbook of Agile Software Craftsmanship
Sách: The art of readable code

4 thoughts on “[Code sao cho chuẩn] Bài 3 – Bàn về comment code.

  1. Pingback: Comment code: viết sao cho CHUẨN? – DLL Blog – Sharing my knowledge

  2. Pingback: [Giới thiệu sách] The art of readable code – Cái tên đã nói lên tất cả – Webbynat

  3. Pingback: [Code sao cho chuẩn] – Bài 1: Hãy bắt đầu nghĩ tới việc viết code đẹp hơn và dễ hiểu hơn. – Những dòng code vui

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s