Có gì sai với ý kiến ​​giải thích mã phức tạp?


236

Rất nhiều người cho rằng "các bình luận nên giải thích" tại sao ", nhưng không phải" làm thế nào "". Những người khác nói rằng "mã nên tự viết tài liệu" và ý kiến ​​nên khan hiếm. Robert C. Martin tuyên bố rằng (thường đọc lại lời của tôi) thường "bình luận là lời xin lỗi cho mã được viết xấu".

Câu hỏi của tôi là như sau:

Có gì sai khi giải thích một thuật toán phức tạp hoặc một đoạn mã dài và phức tạp với một nhận xét mô tả?

Bằng cách này, thay vì các nhà phát triển khác (bao gồm cả chính bạn) phải đọc toàn bộ dòng thuật toán để tìm ra những gì nó làm, họ chỉ có thể đọc nhận xét mô tả thân thiện mà bạn đã viết bằng tiếng Anh.

Tiếng Anh được 'thiết kế' để con người dễ hiểu. Tuy nhiên, Java, Ruby hoặc Perl đã được thiết kế để cân bằng giữa khả năng đọc của con người và khả năng đọc của máy tính, do đó làm ảnh hưởng đến khả năng đọc của con người trong văn bản. Một người có thể hiểu một đoạn tiếng Anh nhanh hơn nhiều mà anh ta / cô ta có thể hiểu một đoạn mã có cùng ý nghĩa (miễn là thao tác không tầm thường).

Vì vậy, sau khi viết một đoạn mã phức tạp được viết bằng ngôn ngữ lập trình có thể đọc được một phần con người, tại sao không thêm một nhận xét mô tả và súc tích giải thích hoạt động của mã bằng tiếng Anh thân thiện và dễ hiểu?

Một số người sẽ nói "mã không nên khó hiểu", "làm cho các hàm nhỏ", "sử dụng tên mô tả", "không viết mã spaghetti".

Nhưng tất cả chúng ta đều biết rằng điều đó là không đủ. Đây chỉ là những hướng dẫn - những hướng dẫn quan trọng và hữu ích - nhưng chúng không thay đổi thực tế là một số thuật toán rất phức tạp. Và do đó rất khó hiểu khi đọc chúng từng dòng một.

Có thực sự tệ khi giải thích một thuật toán phức tạp với một vài dòng nhận xét về hoạt động chung của nó không? Có gì sai khi giải thích mã phức tạp với một bình luận?


14
Nếu nó phức tạp, hãy thử tái cấu trúc nó thành các phần nhỏ hơn.
Vaughan Hilts

151
Về lý thuyết, không có sự khác biệt giữa lý thuyết và thực hành. Trong thực tế, có.
Scott Leadley

5
@mattnz: trực tiếp hơn, tại thời điểm bạn viết bình luận, bạn đang chìm đắm trong vấn đề mà đoạn mã này giải quyết. Lần tới khi bạn truy cập, bạn sẽ có ít khả năng hơn với vấn đề này .
Steve Jessop

26
"Những gì" chức năng hoặc phương thức làm nên rõ ràng từ tên của nó. Làm thế nào nó là rõ ràng từ mã của nó. Tại sao nó được thực hiện theo cách này, những giả định ngầm nào đã được sử dụng, những giấy tờ nào người ta cần đọc để hiểu thuật toán, v.v. - nên được bình luận.
SK-logic

11
Tôi cảm thấy nhiều câu trả lời dưới đây đang cố tình hiểu sai câu hỏi của bạn. Không có gì sai khi bình luận mã của bạn. Nếu bạn cảm thấy bạn cần phải viết một bình luận giải thích, thì bạn cần phải làm.
Tony Enni

Câu trả lời:


408

Trong điều khoản của Giáo dân:

  • Không có gì sai với ý kiến mỗi se. Có gì sai khi viết mã cần những loại bình luận đó, hoặc giả sử rằng bạn có thể viết mã phức tạp miễn là bạn giải thích nó thân thiện bằng tiếng Anh.
  • Nhận xét không tự cập nhật khi bạn thay đổi mã. Đó là lý do tại sao các bình luận thường không đồng bộ với mã.
  • Nhận xét không làm cho mã dễ kiểm tra hơn.
  • Xin lỗi không tệ. Những gì bạn đã làm đòi hỏi phải xin lỗi (viết mã không dễ hiểu) là xấu.
  • Một lập trình viên có khả năng viết mã đơn giản để giải quyết một vấn đề phức tạp tốt hơn một người viết mã phức tạp và sau đó viết một bình luận dài giải thích những gì mã của anh ta làm.

Dòng dưới cùng:

Giải thích cho bản thân là tốt, không cần làm như vậy là tốt hơn.


91
Thường thì không thể biện minh cho việc sử dụng tiền viết lại mã của nhà tuyển dụng để tự giải thích nhiều hơn, khi một bình luận tốt có thể thực hiện công việc trong thời gian ngắn hơn nhiều. Một lập trình viên nghiêm túc phải sử dụng phán đoán của cô ấy / anh ấy mỗi lần.
aeccar

34
@aeccar Viết mã tự giải thích để bắt đầu là tốt hơn.
Tulains Córdova

127
Đôi khi mã tự giải thích không đủ hiệu quả để giải quyết vấn đề với CTNH & SW ngày nay. Và logic kinh doanh nổi tiếng là ... uốn lượn. Tập hợp các vấn đề có giải pháp phần mềm thanh lịch nhỏ hơn đáng kể so với tập hợp các vấn đề hữu ích về mặt kinh tế để giải quyết.
Scott Leadley

62
@rwong: ngược lại, tôi thường thấy mình viết nhiều bình luận hơn trong logic kinh doanh, bởi vì điều quan trọng là phải trình bày chính xác cách mã phù hợp với các yêu cầu đã nêu: "đây là dòng ngăn chặn tất cả chúng ta phải vào tù vì gian lận dây điện trong Phần Dù bộ luật hình sự ". Nếu đó chỉ là một thuật toán, tốt, một lập trình viên có thể tìm ra mục đích từ đầu nếu thực sự cần thiết. Đối với logic kinh doanh, bạn cần một luật sư và khách hàng trong cùng một phòng cùng một lúc. Có thể "ý thức chung" của tôi là ở một miền khác với lập trình viên ứng dụng trung bình ;-)
Steve Jessop

29
@ user61852 Ngoại trừ những gì bạn tự giải thích cho bạn, người vừa viết mã đó và dành khoảng thời gian $ cuối cùng đắm chìm trong nó có thể không tự giải thích cho bạn mà phải duy trì hoặc chỉnh sửa nó trong năm năm kể từ bây giờ, hãy để một mình những người có thể không phải là bạn mà có thể phải nhìn vào nó. "Tự giải thích" là một chén thánh mơ hồ của các định nghĩa.
Shadur

110

Có một loạt các lý do khác nhau để mã trở nên phức tạp hoặc khó hiểu. Các lý do phổ biến nhất được giải quyết tốt nhất bằng cách cấu trúc lại mã để làm cho nó ít gây nhầm lẫn hơn, không phải bằng cách thêm bất kỳ bình luận nào.

Tuy nhiên, có những trường hợp một bình luận được lựa chọn tốt là sự lựa chọn tốt nhất.

  • Nếu đó là thuật toán phức tạp và khó hiểu, thì không chỉ là cách triển khai của nó, loại được viết trong các tạp chí toán học và sau này được gọi là Thuật toán thuật ngữ của Mbogo, sau đó bạn hãy bình luận ngay từ đầu khi thực hiện, đọc đại loại như "Đây là Thuật toán của Mbogo cho các công cụ lọc lại, được mô tả ban đầu ở đây: [URL của giấy]. Việc triển khai này chứa các sàng lọc của Alice và Carol [URL của một bài báo khác]." Đừng cố đi sâu vào chi tiết nào hơn thế; nếu ai đó cần chi tiết hơn có lẽ họ cần đọc toàn bộ bài viết.

  • Nếu bạn đã lấy một cái gì đó có thể được viết thành một hoặc hai dòng trong một ký hiệu chuyên biệt nào đó và mở rộng nó thành một khối lớn của mã mệnh lệnh, đặt một hoặc hai dòng ký hiệu chuyên ngành đó vào một nhận xét bên trên chức năng là một cách tốt để nói với người đọc những gì nó phải làm. Đây là một ngoại lệ đối với "nhưng những gì nếu những nhận xét được không đồng bộ với mã" tranh luận, bởi vì các ký hiệu đặc biệt có lẽ là nhiều dễ dàng hơn để tìm lỗi trong hơn mã. (Thay vào đó là cách khác nếu bạn viết một đặc tả bằng tiếng Anh.) Một ví dụ điển hình là đây: https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.cpp#1057 ...

    /**
     * Scan a unicode-range token.  These match the regular expression
     *
     *     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
     *
     * However, some such tokens are "invalid".  There are three valid forms:
     *
     *     u+[0-9a-f]{x}              1 <= x <= 6
     *     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
     *     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
    
  • Nếu mã đơn giản về tổng thể, nhưng chứa một hoặc hai thứ trông quá phức tạp, không cần thiết hoặc chỉ đơn giản là sai, nhưng phải theo cách đó vì lý do, sau đó bạn đặt một nhận xét ngay trên bit trông đáng ngờ, trong đó bạn nêu lý do . Đây là một ví dụ đơn giản, trong đó điều duy nhất cần giải thích là tại sao một hằng số có một giá trị nhất định.

    /* s1*s2 <= SIZE_MAX if s1 < K and s2 < K, where K = sqrt(SIZE_MAX+1) */
    const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
    if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
        nmemb > 0 && SIZE_MAX / nmemb < size)
      abort();
    

25
Đó là một sự phẫn nộ, 4nên là CHAR_BIT / 2;-)
Steve Jessop

@SteveJessop: Có bất cứ điều gì ngăn cản việc triển khai trong đó CHAR_BITS16 và sizeof (size_t) là 2, nhưng giá trị tối đa của size_t là ví dụ 2 ^ 20 [size_t chứa 12 bit đệm]?
supercat

2
@supercat tôi không thấy bất cứ điều gì mà rõ ràng là ngăn cản rằng trong C99, có nghĩa là ví dụ không chính xác về mặt kỹ thuật. Nó tình cờ được lấy từ (một phiên bản sửa đổi một chút) của OpenBSD reallocarrayvà OpenBSD thường không tin vào việc phục vụ cho các khả năng không xảy ra trong ABI của họ .
zwol

3
@Zack: Nếu mã được thiết kế xung quanh các giả định POSIX, sử dụng CHAR_BITS có thể mang lại ấn tượng rằng mã có thể hoạt động với các giá trị khác 8.
supercat

2
@Zack: Để các loại không dấu có chiều rộng chính xác trở nên hữu ích, ngữ nghĩa của chúng sẽ cần được xác định độc lập với kích thước của int. Như nó là, được đưa ra uint32_t x,y,z;, ý nghĩa của (x-y) > zphụ thuộc vào kích thước của int. Hơn nữa, một ngôn ngữ được thiết kế để viết mã mạnh mẽ sẽ cho phép các lập trình viên phân biệt giữa một loại mà tính toán được dự kiến ​​sẽ vượt quá phạm vi của loại và nên âm thầm bao bọc, so với một trong đó các tính toán vượt quá phạm vi của loại sẽ bị mắc kẹt, so với một trong đó tính toán không dự kiến ​​sẽ vượt quá phạm vi của loại, nhưng ...
supercat

61

Vì vậy, có gì sai khi giải thích mã phức tạp với một bình luận?

Đây không phải là câu hỏi đúng hay sai, mà là 'thực tiễn tốt nhất', như được định nghĩa trong bài viết trên Wikipedia :

Một thực tiễn tốt nhất là một phương pháp hoặc kỹ thuật đã liên tục cho thấy kết quả vượt trội so với những phương pháp đạt được bằng các phương tiện khác và được sử dụng làm điểm chuẩn.

Vì vậy, cách tốt nhất là cố gắng cải thiện mã trước và sử dụng tiếng Anh nếu điều đó là không thể.

Đó không phải là luật, nhưng nó phổ biến hơn nhiều để tìm mã nhận xét yêu cầu tái cấu trúc so với mã được tái cấu trúc yêu cầu nhận xét, thực tiễn tốt nhất phản ánh điều này.


42
+1 cho "nó phổ biến hơn nhiều để tìm mã nhận xét yêu cầu tái cấu trúc so với mã được tái cấu trúc yêu cầu nhận xét"
Brandon

7
Được rồi, nhưng mức độ thường xuyên của nhận xét đó: //This code seriously needs a refactor
Erik Reppen

2
Tất nhiên, bất kỳ cái gọi là thực hành tốt nhất không được hỗ trợ bởi một nghiên cứu khoa học nghiêm ngặt chỉ là một ý kiến.
Blrfl

54

Một ngày sẽ đến khi mã đẹp, được chế tạo hoàn hảo, có cấu trúc tốt và dễ đọc của bạn sẽ không hoạt động. Hoặc nó sẽ không hoạt động đủ tốt. Hoặc một trường hợp đặc biệt sẽ phát sinh khi nó không hoạt động và cần điều chỉnh.

Tại thời điểm đó, bạn sẽ cần phải làm một cái gì đó thay đổi mọi thứ để nó hoạt động chính xác. Đặc biệt trong trường hợp có vấn đề về hiệu năng, nhưng cũng thường xảy ra trong các tình huống trong đó một trong các thư viện, API, dịch vụ web, đá quý hoặc hệ điều hành bạn đang làm việc không hoạt động như mong đợi, cuối cùng bạn có thể đưa ra các đề xuất không nhất thiết là không phù hợp, nhưng là phản trực giác hoặc không rõ ràng.

Nếu bạn không có một số ý kiến ​​để giải thích lý do tại sao bạn chọn cách tiếp cận đó thì rất có khả năng ai đó trong tương lai (và ai đó thậm chí có thể là bạn) sẽ xem mã, xem cách nó có thể được "sửa" một cái gì đó dễ đọc và thanh lịch hơn và vô tình hoàn tác bản sửa lỗi của bạn, bởi vì nó không giống như một bản sửa lỗi.

Nếu mọi người luôn viết mã hoàn hảo thì rõ ràng mã có vẻ không hoàn hảo đang hoạt động xung quanh một số can thiệp khó khăn từ thế giới thực, nhưng đó không phải là cách mọi thứ hoạt động. Hầu hết các lập trình viên thường viết mã khó hiểu hoặc hơi rối, vì vậy khi chúng ta gặp phải điều này, đó là một xu hướng tự nhiên để dọn dẹp nó. Tôi thề rằng quá khứ của tôi là một thằng ngốc thực sự bất cứ khi nào tôi đọc mã cũ tôi đã viết.

Vì vậy, tôi không nghĩ rằng các bình luận là một lời xin lỗi cho mã xấu, nhưng có thể là một lời giải thích cho lý do tại sao bạn không làm điều rõ ràng. Có // The standard approach doesn't work against the 64 bit version of the Frobosticate Librarysẽ cho phép các nhà phát triển trong tương lai, bao gồm cả bản thân tương lai của bạn, chú ý đến phần mã đó và kiểm tra thư viện đó. Chắc chắn, bạn cũng có thể đặt các bình luận trong kiểm soát nguồn của mình, nhưng mọi người sẽ chỉ nhìn vào những bình luận đó sau khi có sự cố xảy ra. Họ sẽ đọc các bình luận mã khi họ thay đổi mã.

Những người nói với chúng ta rằng chúng ta nên luôn luôn viết mã hoàn hảo về mặt lý thuyết không phải lúc nào cũng là những người có nhiều kinh nghiệm lập trình trong môi trường thế giới thực. Đôi khi bạn cần viết mã thực hiện đến một mức nhất định, đôi khi bạn cần phải tương tác với các hệ thống không hoàn hảo. Điều đó không có nghĩa là bạn không thể làm điều này theo những cách viết thanh lịch và tốt, nhưng các giải pháp không rõ ràng cần giải thích.

Khi tôi viết mã cho các dự án sở thích mà tôi không biết ai sẽ đọc, tôi vẫn bình luận những phần mà tôi thấy khó hiểu - ví dụ, bất kỳ hình học 3D nào cũng liên quan đến toán học mà tôi không hoàn toàn ở nhà - vì tôi biết khi tôi quay lại trong sáu tháng tôi sẽ hoàn toàn quên mất cách làm công cụ này. Đó không phải là lời xin lỗi cho mã xấu, đó là sự thừa nhận về giới hạn cá nhân. Tất cả những gì tôi sẽ làm bằng cách để nó không bị lỗi là tạo ra nhiều công việc hơn cho bản thân trong tương lai. Tôi không muốn bản thân tương lai của mình phải học lại một cái gì đó không cần thiết nếu tôi có thể tránh nó ngay bây giờ. Giá trị nào có thể có?


5
@Christian phải không? Dòng đầu tiên tham chiếu tuyên bố đó, chắc chắn, nhưng ngoài ra nó rộng hơn một chút khi tôi hiểu nó.
glenatron

9
"Tôi thề rằng quá khứ của tôi là một thằng ngốc thực sự bất cứ khi nào tôi đọc mã cũ tôi đã viết." Bốn năm trong sự nghiệp phát triển của tôi và tôi thấy đây là một sự cố xảy ra bất cứ khi nào tôi nhìn vào bất cứ thứ gì cũ hơn 6 tháng.
Ken

6
Trong nhiều trường hợp, thông tin lịch sử hữu ích và hữu ích nhất liên quan đến những điều được xem xét nhưng quyết định chống lại. Có nhiều trường hợp ai đó chọn cách tiếp cận X cho một cái gì đó và một số cách tiếp cận khác Y có vẻ tốt hơn; trong một số trường hợp đó, Y sẽ "gần như" hoạt động tốt hơn X, nhưng hóa ra lại có một số vấn đề không thể vượt qua. Nếu Y bị tránh vì những vấn đề đó, kiến ​​thức như vậy có thể giúp ngăn người khác lãng phí thời gian của họ vào những nỗ lực không thành công để thực hiện phương pháp tiếp cận Y.
supercat

4
Trên cơ sở hàng ngày, tôi cũng sử dụng công việc trong các bình luận tiến bộ rất nhiều - chúng không ở đó lâu dài, nhưng việc ghi chú TODO hoặc một phần ngắn để nhắc nhở tôi những gì tôi sẽ làm tiếp theo có thể hữu ích nhắc nhở vào buổi sáng.
glenatron

1
@Lilienthal, tôi không nghĩ rằng đoạn cuối bị giới hạn trong các dự án cá nhân, anh ấy nói "... Tôi vẫn bình luận những phần mà tôi thấy khó hiểu."
Wildcard

29

Nhu cầu bình luận tỷ lệ nghịch với mức độ trừu tượng của mã.

Ví dụ, ngôn ngữ hội là, đối với hầu hết các mục đích thực tế, không thể hiểu được mà không có ý kiến. Đây là một đoạn trích từ một chương trình nhỏ có thể tính toán và in các thuật ngữ của chuỗi Fibonacci :

main:   
; initializes the two numbers and the counter.  Note that this assumes
; that the counter and num1 and num2 areas are contiguous!
;
    mov ax,'00'                     ; initialize to all ASCII zeroes
    mov di,counter                  ; including the counter
    mov cx,digits+cntDigits/2       ; two bytes at a time
    cld                             ; initialize from low to high memory
    rep stosw                       ; write the data
    inc ax                          ; make sure ASCII zero is in al
    mov [num1 + digits - 1],al      ; last digit is one
    mov [num2 + digits - 1],al      ; 
    mov [counter + cntDigits - 1],al

    jmp .bottom         ; done with initialization, so begin

.top
    ; add num1 to num2
    mov di,num1+digits-1
    mov si,num2+digits-1
    mov cx,digits       ; 
    call    AddNumbers  ; num2 += num1
    mov bp,num2         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jz  .done           ;

    ; add num2 to num1
    mov di,num2+digits-1
    mov si,num1+digits-1
    mov cx,digits       ;
    call    AddNumbers  ; num1 += num2
.bottom
    mov bp,num1         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jnz .top            ;
.done
    call    CRLF        ; finish off with CRLF
    mov ax,4c00h        ; terminate
    int 21h             ;

Ngay cả với các bình luận, nó có thể khá phức tạp để mò mẫm.

Ví dụ hiện đại: Regexes thường là các cấu trúc trừu tượng rất thấp (chữ thường, số 0, 1, 2, dòng mới, v.v.). Họ có thể cần bình luận dưới dạng mẫu (Bob Martin, IIRC, không thừa nhận điều này). Đây là một biểu thức chính mà (tôi nghĩ) phải phù hợp với các URL HTTP (S) và FTP:

^(((ht|f)tp(s?))\://)?(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|edu|gov|m
+il|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(/($|[a-zA-Z0-9\.
+\,\;\?\'\\\+&amp;%\$#\=~_\-]+))*$

Khi các ngôn ngữ tăng lên hệ thống phân cấp trừu tượng, lập trình viên có thể sử dụng các tóm tắt gợi mở (tên biến, tên hàm, tên lớp, tên mô-đun, giao diện, gọi lại, v.v.) để cung cấp tài liệu tích hợp. Bỏ bê để tận dụng lợi thế này, và sử dụng các bình luận để viết lên nó là sự lười biếng, một sự bất đồng và thiếu tôn trọng người bảo trì.

Tôi đang nghĩ đến việc Bí quyết Numerical trong C dịch chủ yếu là nguyên văn để Bí quyết Numerical trong C ++ , mà tôi suy ra bắt đầu như Bí quyết Numerical (trong FORTAN), với tất cả các biến a, aa, b, c, cc, vv được duy trì qua từng phiên bản. Các thuật toán có thể đã đúng, nhưng chúng không tận dụng được sự trừu tượng mà các ngôn ngữ cung cấp. Và họ làm tôi thất vọng. Mẫu từ một bài viết của Tiến sĩ Dobbs - Biến đổi Fourier nhanh :

void four1(double* data, unsigned long nn)
{
    unsigned long n, mmax, m, j, istep, i;
    double wtemp, wr, wpr, wpi, wi, theta;
    double tempr, tempi;

    // reverse-binary reindexing
    n = nn<<1;
    j=1;
    for (i=1; i<n; i+=2) {
        if (j>i) {
            swap(data[j-1], data[i-1]);
            swap(data[j], data[i]);
        }
        m = nn;
        while (m>=2 && j>m) {
            j -= m;
            m >>= 1;
        }
        j += m;
    };

    // here begins the Danielson-Lanczos section
    mmax=2;
    while (n>mmax) {
        istep = mmax<<1;
        theta = -(2*M_PI/mmax);
        wtemp = sin(0.5*theta);
        wpr = -2.0*wtemp*wtemp;
        wpi = sin(theta);
        wr = 1.0;
        wi = 0.0;
        for (m=1; m < mmax; m += 2) {
            for (i=m; i <= n; i += istep) {
                j=i+mmax;
                tempr = wr*data[j-1] - wi*data[j];
                tempi = wr * data[j] + wi*data[j-1];

                data[j-1] = data[i-1] - tempr;
                data[j] = data[i] - tempi;
                data[i-1] += tempr;
                data[i] += tempi;
            }
            wtemp=wr;
            wr += wr*wpr - wi*wpi;
            wi += wi*wpr + wtemp*wpi;
        }
        mmax=istep;
    }
}

Trong trường hợp đặc biệt về tính trừu tượng, mọi ngôn ngữ đều có các thành ngữ / đoạn mã chính tắc cho một số tác vụ phổ biến nhất định (xóa danh sách liên kết động trong C) và bất kể chúng trông như thế nào, chúng không nên được ghi lại. Các lập trình viên nên học những thành ngữ này, vì chúng không phải là một phần không chính thức của ngôn ngữ.

Vì vậy, mang đi: Mã không thành ngữ được xây dựng từ các khối xây dựng cấp thấp không thể tránh được nhu cầu bình luận. Và điều này là cần thiết WAAAAY ít hơn nó xảy ra.


1
Không ai thực sự nên viết một dòng như thế này bằng ngôn ngữ lắp ráp : dec dword [term] ; decrement loop counter. Mặt khác, ví dụ về ngôn ngữ lắp ráp của bạn bị thiếu là một nhận xét trước mỗi "đoạn mã" giải thích khối mã tiếp theo làm gì. Trong trường hợp đó, nhận xét thường sẽ tương đương với một dòng trong mã giả, chẳng hạn như ;clear the screen, theo sau là 7 dòng thực sự cần để xóa màn hình.
Scott Whitlock

1
Vâng, có những gì tôi sẽ xem xét một số ý kiến ​​không cần thiết trong mẫu lắp ráp, nhưng công bằng mà nói, nó là đại diện đẹp của phong cách hội 'Tốt'. Ngay cả với một đoạn mở đầu một hoặc hai dòng, mã sẽ rất khó theo dõi. Tôi hiểu mẫu ASM tốt hơn ví dụ FFT. Tôi đã lập trình một FFT trong C ++ ở trường học, và nó không giống bất cứ thứ gì như thế này, nhưng sau đó chúng tôi đã sử dụng STL, iterators, functor một vài cuộc gọi phương thức. Không nhanh như chức năng nguyên khối, nhưng dễ đọc hơn rất nhiều. Tôi sẽ cố gắng thêm nó để tương phản với mẫu NRinC ++.
Kristian H

Ý bạn là ^(((ht|f)tps?)\:\/\/)?(www\.)*[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$sao Hãy nhận biết các địa chỉ số.
izabera

Quan điểm của tôi nhiều hay ít: một số thứ được xây dựng từ sự trừu tượng ở mức độ rất thấp không dễ đọc hoặc xác minh. Nhận xét (và, không để quá xa, TESTS) có thể hữu ích và không gây bất lợi. Đồng thời, việc không sử dụng trừu tượng cấp cao hơn có sẵn (: alpha :: num: nếu có) khiến bạn khó hiểu hơn, ngay cả với những bình luận tốt, hơn là sử dụng trừu tượng cấp cao hơn.
Kristian H

3
+1: "The need for comments is inversely proportional to the abstraction level of the code." Khá nhiều tổng hợp tất cả mọi thứ ngay tại đó.
Gerrat

21

Tôi không tin có gì sai với các bình luận trong mã. Ý kiến ​​cho rằng ý kiến ​​nào đó không tốt theo ý kiến ​​của tôi là do một số lập trình viên đưa mọi thứ đi quá xa. Có rất nhiều bandwagoning trong ngành công nghiệp này, đặc biệt là về quan điểm cực đoan. Một nơi nào đó trên đường đi đã nhận xét mã trở nên tương đương với mã xấu và tôi không chắc tại sao.

Nhận xét có vấn đề - bạn cần cập nhật chúng khi bạn cập nhật mã mà chúng đề cập đến, điều này xảy ra quá ít khi xảy ra. Một wiki hoặc một cái gì đó là một tài nguyên phù hợp hơn cho tài liệu kỹ lưỡng về mã của bạn. Mã của bạn nên được đọc mà không cần bình luận. Kiểm soát phiên bản hoặc ghi chú sửa đổi phải là nơi bạn mô tả các thay đổi mã bạn đã thực hiện.

Tuy nhiên, không có điều nào ở trên làm mất hiệu lực sử dụng các bình luận. Chúng ta không sống trong một thế giới lý tưởng nên khi bất kỳ điều nào ở trên thất bại vì bất kỳ lý do gì, tôi muốn có một số ý kiến ​​để quay trở lại.


18

Tôi nghĩ rằng bạn đang đọc quá nhiều vào những gì anh ấy nói. Có hai phần riêng biệt để khiếu nại của bạn:

Có gì sai khi giải thích (1) một thuật toán phức tạp hoặc (2) một đoạn mã dài và phức tạp với một nhận xét mô tả?

(1) là không thể tránh khỏi. Tôi không nghĩ rằng Martin sẽ không đồng ý với bạn. Nếu bạn đang viết một cái gì đó như căn bậc hai nghịch đảo nhanh , bạn sẽ cần một số nhận xét, ngay cả khi đó chỉ là "hack cấp độ điểm bit nổi." Chặn thứ gì đó đơn giản như DFS hoặc tìm kiếm nhị phân, không chắc là người đọc mã của bạn sẽ có kinh nghiệm với thuật toán đó, và vì vậy tôi nghĩ nên có ít nhất một đề cập trong các nhận xét về nó là gì.

Hầu hết các mã không (1), tuy nhiên. Hiếm khi bạn viết một phần mềm mà không có gì ngoài việc triển khai mutex bằng tay, các hoạt động đại số tuyến tính tối nghĩa với sự hỗ trợ của thư viện kém và các thuật toán mới chỉ được biết đến với nhóm nghiên cứu của công ty bạn. Hầu hết các mã bao gồm các lệnh gọi thư viện / khung / API, IO, soạn sẵn và kiểm tra đơn vị.

Đây là loại mã mà Martin đang nói đến. Và anh ấy giải quyết câu hỏi của bạn với trích dẫn từ Kernighan và Plaugher ở đầu chương:

Đừng bình luận mã xấu viết lại nó.

Nếu bạn có các phần dài, phức tạp trong mã của mình, bạn đã không giữ được mã của mình sạch sẽ . Giải pháp tốt nhất cho vấn đề này không phải là viết một bình luận dài đoạn ở đầu tệp để giúp các nhà phát triển trong tương lai tìm hiểu về nó; giải pháp tốt nhất là viết lại nó.

Và đây chính xác là những gì Martin nói:

Việc sử dụng đúng các bình luận là để bù đắp cho sự thất bại của chúng tôi trong việc thể hiện chính chúng ta trong mã ... Nhận xét luôn là thất bại. Chúng ta phải có chúng bởi vì chúng ta không thể luôn luôn tìm ra cách thể hiện bản thân mà không có chúng, nhưng việc sử dụng chúng không phải là một nguyên nhân cho lễ kỷ niệm.

Đây là của bạn (2). Martin đồng ý rằng mã dài, phức tạp cần có ý kiến ​​- nhưng anh ta đổ lỗi cho mã đó trên vai của lập trình viên đã viết nó, không phải là một ý tưởng mơ hồ rằng "tất cả chúng ta đều biết rằng điều đó là không đủ." Ông lập luận rằng:

Mã rõ ràng và biểu cảm với ít bình luận vượt trội hơn nhiều so với mã lộn xộn và phức tạp với nhiều bình luận. Thay vì dành thời gian của bạn để viết các bình luận giải thích về mớ hỗn độn bạn đã tạo ra, hãy dành nó để dọn dẹp mớ hỗn độn đó.


3
Nếu một nhà phát triển mà tôi đang làm việc chỉ đơn giản là đã viết "hack cấp độ bit dấu phẩy động" để giải thích thuật toán căn bậc hai nhanh - họ sẽ nói chuyện với tôi. Miễn là họ bao gồm một tài liệu tham khảo đến một nơi nào đó hữu ích hơn, tôi sẽ rất vui.
Michael Anderson

8
Tôi không đồng ý theo một cách - một bình luận giải thích làm thế nào một cái gì đó xấu hoạt động nhanh hơn rất nhiều. Đưa ra một số mã có khả năng không bị chạm lại (hầu hết mã tôi đoán) thì một nhận xét là một giải pháp kinh doanh tốt hơn so với tái cấu trúc lớn, thường đưa ra các lỗi (như một cách khắc phục lỗi giết chết dựa vào lỗi vẫn là một lỗi). Một thế giới hoàn hảo của mã hoàn toàn dễ hiểu không có sẵn cho chúng tôi.
gbjbaanb

2
@trysis haha, vâng, nhưng trong một thế giới nơi các lập trình viên có trách nhiệm và không phải là doanh nhân, họ sẽ không bao giờ giao hàng vì họ mãi mãi mạ vàng một codebase được tái cấu trúc liên tục trong một cuộc tìm kiếm sự hoàn hảo.
gbjbaanb

4
@PatrickCollins gần như tất cả mọi thứ tôi đọc trên web là về việc thực hiện nó ngay lần đầu tiên. Hầu như không ai muốn viết bài về sửa chữa mớ hỗn độn! Các nhà vật lý nói "được đưa ra một hình cầu hoàn hảo ..." Comp.Scientists nói "cho một sự phát triển greenfield ..."
gbjbaanb

2
Giải pháp tốt nhất là viết lại nó trong thời gian vô hạn; nhưng đưa ra cơ sở mã của người khác, thời hạn công ty điển hình và thực tế; đôi khi điều tốt nhất để làm là bình luận, thêm TODO: Refactor và đưa bộ tái cấu trúc đó vào bản phát hành tiếp theo; và sửa chữa đó cần phải được thực hiện ngày hôm qua được thực hiện bây giờ. Điều về tất cả các cuộc nói chuyện lý tưởng này về việc tái cấu trúc là nó không giải thích được cách mọi thứ thực sự hoạt động ở nơi làm việc; đôi khi có những ưu tiên cao hơn và thời hạn đủ sớm sẽ ưu tiên sửa lỗi mã chất lượng kém kế thừa. Đó chỉ là cách nó được.
hsanders

8

Có gì sai khi giải thích một thuật toán phức tạp hoặc một đoạn mã dài và phức tạp với một nhận xét mô tả?

Không có gì là như vậy. Tài liệu công việc của bạn là thực hành tốt.

Điều đó nói rằng, bạn có một sự phân đôi giả ở đây: viết mã sạch so với viết mã tài liệu - hai thứ không đối lập nhau.

Những gì bạn nên tập trung vào là đơn giản hóa và trừu tượng hóa mã phức tạp thành mã đơn giản hơn, thay vì nghĩ "mã phức tạp là tốt miễn là nó được nhận xét".

Lý tưởng nhất, mã của bạn nên đơn giản và được ghi lại.

Bằng cách này, thay vì các nhà phát triển khác (bao gồm cả chính bạn) phải đọc toàn bộ dòng thuật toán để tìm ra những gì nó làm, họ chỉ có thể đọc nhận xét mô tả thân thiện mà bạn đã viết bằng tiếng Anh.

Thật. Đây là lý do tại sao tất cả các thuật toán API công khai của bạn nên được giải thích trong tài liệu.

Vì vậy, sau khi viết một đoạn mã phức tạp được viết bằng ngôn ngữ lập trình có thể đọc được một phần con người, tại sao không thêm một nhận xét mô tả và súc tích giải thích hoạt động của mã bằng tiếng Anh thân thiện và dễ hiểu?

Lý tưởng nhất là sau khi viết một đoạn mã phức tạp, bạn nên (không phải là một danh sách đầy đủ):

  • coi đó là một bản nháp (tức là kế hoạch viết lại nó)
  • chính thức hóa các điểm nhập / giao diện / vai trò / thuật toán (phân tích và tối ưu hóa giao diện, chính thức hóa trừu tượng, điều kiện tiên quyết tài liệu, hậu điều kiện và tác dụng phụ và các trường hợp lỗi tài liệu).
  • viết bài kiểm tra
  • dọn dẹp và tái cấu trúc

Không có bước nào trong số này là tầm thường để làm (tức là mỗi bước có thể mất vài giờ) và phần thưởng cho việc thực hiện chúng không phải là ngay lập tức. Do đó, các bước này (hầu như) luôn bị xâm phạm (bởi các nhà phát triển cắt góc, quản lý cắt góc, thời hạn, hạn chế thị trường / điều kiện thực tế khác, thiếu kinh nghiệm, v.v.).

[...] Một số thuật toán rất phức tạp. Và do đó rất khó hiểu khi đọc chúng từng dòng một.

Bạn không bao giờ phải dựa vào việc đọc triển khai để tìm ra API làm gì. Khi bạn làm điều đó, bạn đang triển khai mã máy khách dựa trên việc triển khai (thay vì giao diện) và điều đó có nghĩa là khớp nối mô-đun của bạn đã bị bắn vào địa ngục, bạn có khả năng đưa ra các phụ thuộc không có giấy tờ với mỗi dòng mã mới mà bạn viết và đã thêm nợ kỹ thuật.

Có thực sự tệ khi giải thích một thuật toán phức tạp với một vài dòng nhận xét về hoạt động chung của nó không?

Không - đó là tốt. Thêm một vài dòng ý kiến ​​là không đủ mặc dù.

Có gì sai khi giải thích mã phức tạp với một bình luận?

Thực tế là bạn không nên có mã phức tạp, nếu điều đó có thể tránh được.

Để tránh mã phức tạp, hãy chính thức hóa giao diện của bạn, chi tiêu gấp 8 lần cho thiết kế API so với chi tiêu cho việc triển khai (Stepanov đề xuất chi ít nhất 10 lần cho giao diện, so với triển khai) và đi vào phát triển dự án với kiến ​​thức mà bạn đang tạo một dự án, không chỉ viết một số thuật toán.

Một dự án liên quan đến tài liệu API, tài liệu chức năng, đo lường mã / chất lượng, quản lý dự án, v.v. Không có quy trình nào trong số này là một lần, các bước nhanh để thực hiện (tất cả đều mất thời gian, đòi hỏi phải suy nghĩ và lập kế hoạch, và tất cả đều yêu cầu bạn quay lại với chúng theo định kỳ và sửa đổi / hoàn thành chúng với các chi tiết).


3
"Bạn không bao giờ phải dựa vào việc đọc triển khai để tìm ra API làm gì." Đôi khi điều này gây ra cho bạn bởi một thượng nguồn mà bạn cam kết sử dụng. Tôi đã có một dự án đặc biệt không hài lòng với các bình luận có dạng "mã Heath Robinson xấu xí sau tồn tại bởi vì SimpleAPI () không hoạt động đúng trên phần cứng này mặc dù nhà cung cấp tuyên bố gì".
pjc50

6

thay vì các nhà phát triển khác (bao gồm cả chính bạn) phải đọc toàn bộ dòng thuật toán để tìm hiểu những gì nó làm, họ chỉ có thể đọc nhận xét mô tả thân thiện mà bạn đã viết bằng tiếng Anh.

Tôi sẽ coi đây là một sự lạm dụng nhẹ của "ý kiến". Nếu lập trình viên muốn đọc một cái gì đó thay vì toàn bộ thuật toán, thì đó là tài liệu chức năng dành cho. OK, vì vậy tài liệu hàm có thể thực sự xuất hiện trong các bình luận trong nguồn (có lẽ để trích xuất bằng các công cụ doc), nhưng mặc dù về mặt cú pháp, đó là một nhận xét khi có liên quan đến trình biên dịch của bạn, bạn nên xem xét chúng những thứ riêng biệt với mục đích riêng biệt. Tôi không nghĩ rằng "ý kiến ​​nên khan hiếm" nhất thiết phải có nghĩa là "tài liệu nên khan hiếm" hoặc thậm chí "thông báo bản quyền nên khan hiếm"!

Nhận xét trong chức năng là để ai đó đọc cũng như mã. Vì vậy, nếu bạn có một vài dòng trong mã khó hiểu và bạn không thể làm cho chúng dễ hiểu, thì một nhận xét sẽ hữu ích cho người đọc sử dụng làm trình giữ chỗ cho các dòng đó. Điều này có thể rất hữu ích trong khi người đọc chỉ đang cố gắng để có được ý chính chung, nhưng có một vài vấn đề:

  • Nhận xét không nhất thiết đúng, trong khi mã thực hiện những gì nó làm. Vì vậy, người đọc đang dùng từ của bạn cho nó, và điều này không lý tưởng.
  • Người đọc chưa hiểu chính mã, vì vậy cho đến khi họ quay lại mã sau, họ vẫn không đủ điều kiện để sửa đổi hoặc sử dụng lại mã. Trong trường hợp họ đang làm gì đọc nó?

Có những trường hợp ngoại lệ, nhưng hầu hết người đọc sẽ cần phải hiểu chính mã. Bình luận nên được viết để hỗ trợ điều đó, không thay thế nó, đó là lý do tại sao bạn thường khuyên rằng các bình luận nên nói "tại sao bạn lại làm như vậy". Một người đọc biết động lực cho một vài dòng mã tiếp theo có cơ hội tốt hơn để xem những gì họ làm và làm thế nào.


5
Một nơi hữu ích để bình luận: trong mã khoa học, bạn thường có thể có các tính toán khá phức tạp, liên quan đến rất nhiều biến. Đối với sự tỉnh táo của lập trình viên, sẽ rất hợp lý khi giữ các tên biến thực sự ngắn, vì vậy bạn có thể nhìn vào toán học, thay vì tên. Nhưng điều đó làm cho nó thực sự khó hiểu cho người đọc. Vì vậy, một mô tả ngắn về những gì đang xảy ra (hoặc tốt hơn, một tham chiếu đến phương trình trong một bài báo hoặc tương tự), có thể thực sự hữu ích.
ness101

1
@ naught101: có, đặc biệt là vì bài báo mà bạn đang đề cập cũng có thể đã sử dụng tên biến một chữ cái. Thông thường sẽ dễ dàng hơn khi thấy rằng mã thực sự tuân theo giấy nếu bạn sử dụng cùng tên, nhưng điều đó mâu thuẫn với mục tiêu của mã là tự giải thích (thay vào đó, nó được giải thích bằng giấy ). Trong trường hợp này, một nhận xét trong đó mỗi tên được xác định, cho biết ý nghĩa thực sự của nó, thay thế cho các tên có ý nghĩa.
Steve Jessop

1
Khi tôi đang tìm kiếm một cái gì đó cụ thể trong mã (trường hợp cụ thể này được xử lý ở đâu?), Tôi không muốn đọc và hiểu các đoạn mã chỉ để khám phá rằng rốt cuộc đó không phải là nơi. Tôi cần những bình luận tóm tắt trong một dòng duy nhất những gì đoạn tiếp theo đang làm. Bằng cách này, tôi sẽ nhanh chóng xác định vị trí các phần của mã liên quan đến vấn đề của mình và bỏ qua các chi tiết không thú vị.
Florian F

1
@FlorianF: phản hồi truyền thống là tên biến và hàm sẽ biểu thị đại khái nội dung của mã, và do đó cho phép bạn đọc lướt qua những thứ chắc chắn không phải về những gì bạn đang tìm kiếm. Tôi đồng ý với bạn rằng điều này không phải lúc nào cũng thành công, nhưng tôi không đồng ý mạnh mẽ đến mức tôi nghĩ rằng tất cả các mã cần phải được nhận xét để hỗ trợ tìm kiếm hoặc đọc lướt qua. Nhưng bạn nói đúng, đó là trường hợp ai đó đang đọc mã của bạn (loại) và hợp pháp không cần phải hiểu nó.
Steve Jessop

2
@Snowman Mọi người có thể làm điều đó với tên biến. Tôi đã thấy mã trong đó listOfApples có chứa danh sách Chuối. Ai đó đã sao chép mã xử lý danh sách Táo và điều chỉnh nó cho Chuối mà không cần thay đổi tên biến.
Florian F

5

Thường thì chúng ta phải làm những việc phức tạp. Chắc chắn là đúng khi ghi lại chúng cho sự hiểu biết trong tương lai. Đôi khi vị trí thích hợp cho tài liệu này là trong mã, nơi tài liệu có thể được cập nhật với mã. Nhưng nó chắc chắn đáng xem xét tài liệu riêng biệt. Điều này cũng có thể dễ dàng hơn để trình bày cho người khác, bao gồm sơ đồ, hình ảnh màu, vv. Sau đó, nhận xét chỉ là:

// This code implements the algorithm described in requirements document 239.

hoặc thậm chí chỉ

void doPRD239Algorithm() { ...

Chắc chắn mọi người đều hài lòng với chức năng đặt tên MatchStringKnuthMorrisPratthay encryptAEShay partitionBSP. Nhiều cái tên khó hiểu hơn đáng để giải thích trong một bình luận. Bạn cũng có thể thêm dữ liệu thư mục và liên kết đến một bài báo mà bạn đã triển khai thuật toán từ đó.

Nếu một thuật toán phức tạp và mới lạ và không rõ ràng, nó chắc chắn có giá trị một tài liệu, ngay cả khi chỉ dành cho lưu thông nội bộ công ty. Kiểm tra tài liệu vào kiểm soát nguồn nếu bạn lo lắng về việc nó bị mất.

Có một loại mã khác không quá nhiều thuật toán là quan liêu. Bạn cần thiết lập các tham số cho một hệ thống khác hoặc tương tác với các lỗi của người khác:

/* Configure the beam controller and turn on the laser.
The sequence is timing-critical and this code must run with interrupts disabled.
Note that the constant 0xef45ab87 differs from the vendor documentation; the vendor
is wrong in this case.
Some of these operations write the same value multiple times. Do not attempt
to optimise this code by removing seemingly redundant operations.
*/

2
Tôi tranh luận về việc đặt tên hàm / phương thức theo thuật toán bên trong của chúng, phần lớn thời gian phương thức được sử dụng phải là mối quan tâm nội bộ, bằng mọi cách có nghĩa là tài liệu đứng đầu chức năng của bạn với phương thức được sử dụng, nhưng đừng gọi nó doPRD239Algorithmlà cho tôi biết Không có gì về hàm mà không phải tìm kiếm các thuật toán, lý do MatchStringKnuthMorrisPrattencryptAEScông việc là chúng bắt đầu bằng một mô tả về những gì chúng làm, sau đó tiếp theo với một mô tả về phương pháp luận.
scragar

5

Tôi quên nơi tôi đọc nó nhưng có một dòng sắc nét và rõ ràng giữa những gì sẽ xuất hiện trong mã của bạn và những gì sẽ xuất hiện như một nhận xét.

Tôi tin rằng bạn nên bình luận ý định của bạn, không phải thuật toán của bạn . Tức là bình luận những gì bạn muốn làm, không phải những gì bạn làm .

Ví dụ:

// The getter.
public <V> V get(final K key, Class<V> type) {
  // Has it run yet?
  Future<Object> f = multitons.get(key);
  if (f == null) {
    // No! Make the task that runs it.
    FutureTask<Object> ft = new FutureTask<Object>(
            new Callable() {

              public Object call() throws Exception {
                // Only do the create when called to do so.
                return key.create();
              }

            });
    // Only put if not there.
    f = multitons.putIfAbsent(key, ft);
    if (f == null) {
      // We replaced null so we successfully put. We were first!
      f = ft;
      // Initiate the task.
      ft.run();
    }
  }
  try {
    /**
     * If code gets here and hangs due to f.status = 0 (FutureTask.NEW)
     * then you are trying to get from your Multiton in your creator.
     *
     * Cannot check for that without unnecessarily complex code.
     *
     * Perhaps could use get with timeout.
     */
    // Cast here to force the right type.
    return (V) f.get();
  } catch (Exception ex) {
    // Hide exceptions without discarding them.
    throw Throwables.asRuntimeException(ex);
  }
}

Ở đây không có nỗ lực để nêu những gì mỗi bước thực hiện, tất cả những gì nó nêu là những gì nó phải làm.

Tái bút: Tôi đã tìm thấy nguồn mà tôi đang đề cập đến - Mã hóa kinh dị: Mã cho bạn biết làm thế nào, Nhận xét cho bạn biết tại sao


8
Nhận xét đầu tiên: Nó đã chạy chưa? Có cái gì chạy chưa? Tương tự cho các ý kiến ​​khác. Đối với một người không biết những gì mã làm, điều này là vô ích.
gnasher729

1
@ gnasher729 - Lấy ra khỏi bối cảnh hầu như mọi bình luận sẽ vô ích - mã này là một minh chứng cho việc thêm các bình luận chỉ ra ý định thay vì cố gắng mô tả . Tôi xin lỗi vì nó không làm gì cho bạn.
OldCurmudgeon

2
Một người duy trì mã đó sẽ không có bối cảnh. Nó không đặc biệt khó khăn để tìm ra những gì mã làm, nhưng các ý kiến ​​không giúp đỡ. Nếu bạn viết bình luận, hãy dành thời gian và tập trung khi bạn viết chúng.
gnasher729

BTW - Nhận xét đã chạy chưa, đang đề cập đến Futurevà chỉ ra rằng việc get()kiểm tra tiếp theo sẽ nullphát hiện xem liệu Futuređã được chạy hay chưa - ghi lại chính xác ý định thay vì quá trình .
OldCurmudgeon

1
@OldCurmudgeon: Câu trả lời của bạn đủ gần với những gì tôi đang nghĩ, rằng tôi sẽ chỉ thêm nhận xét này như một ví dụ về quan điểm của bạn. Mặc dù một bình luận không cần thiết để giải thích mã sạch, một bình luận thật tốt để giải thích tại sao việc mã hóa được thực hiện MỘT CÁCH TRÊN KHÁC. Theo kinh nghiệm hạn chế của tôi, các bình luận thường hữu ích để giải thích các đặc điểm riêng của bộ dữ liệu mà mã đang hoạt động hoặc các quy tắc kinh doanh mà mã có nghĩa là để thực thi. Mã nhận xét được thêm vào để sửa lỗi là một ví dụ tốt, nếu lỗi đó xảy ra do giả định về dữ liệu là sai.
Randall Stewart

4

Nhưng chúng ta đều biết rằng điều đó là không đủ.

Có thật không? Kể từ khi?

Mã được thiết kế tốt với tên tốt là quá đủ trong phần lớn các trường hợp. Các đối số chống lại việc sử dụng các bình luận được biết đến và được ghi lại (như bạn đề cập).

Nhưng đây là những hướng dẫn (giống như bất cứ điều gì khác). Trong trường hợp hiếm hoi (theo kinh nghiệm của tôi, cứ khoảng 2 năm một lần), mọi thứ sẽ tồi tệ hơn khi được tái cấu trúc thành các chức năng rõ ràng nhỏ hơn (do nhu cầu về hiệu suất hoặc sự gắn kết), hãy tiếp tục - đưa ra một số nhận xét dài dòng giải thích điều gì thực sự là làm (và tại sao bạn vi phạm các thực tiễn tốt nhất).


7
Tôi biết nó là không đủ.
Florian F

2
Kể từ khi? Rõ ràng, bạn đã biết câu trả lời cho điều đó. "Mã được thiết kế tốt với tên tốt là quá đủ trong phần lớn các trường hợp." Vì vậy, nó có thể không đủ trong một số ít các trường hợp, đó chính xác là những gì người hỏi đang hỏi.
Ellesedil

3
Tôi đã từng cố gắng giải mã mã của những người khác mà tôi muốn đã thêm một số nhận xét nhiều hơn hai năm một lần.
Ogre Psalm33

@ OgrePsalm33 - Họ có phương pháp nhỏ và sử dụng tên hay không? Mã xấu là xấu, bất kể ý kiến.
Telastyn

2
@Telastyn Thật không may, khi làm việc trên cơ sở mã lớn, các phương thức "nhỏ" và tên "tốt" là chủ quan đối với mỗi nhà phát triển (vì vậy đó là một nhận xét tốt cho vấn đề đó). Một nhà phát triển viết mã thuật toán xử lý đồ họa Flarbigan trong 7 năm, có thể viết một cái gì đó hoàn toàn rõ ràng cho anh ta và các nhà phát triển tương tự, nhưng sẽ khó hiểu với anh chàng mới đã dành 4 năm qua để phát triển mã cơ sở hạ tầng lưới Perbian. Sau đó, 2 tuần sau, chuyên gia Flarbigan bỏ việc.
Ogre Psalm33

2

Mục đích chính của mã là ra lệnh cho máy tính làm điều gì đó, vì vậy một bình luận tốt không bao giờ thay thế cho mã tốt vì các bình luận không thể được thực thi.

Điều đó đang được nói, ý kiến ​​trong nguồn là một dạng tài liệu cho các lập trình viên khác (bao gồm cả chính bạn). Nếu các bình luận liên quan đến nhiều vấn đề trừu tượng hơn những gì mã đang làm ở mỗi bước, thì bạn đang làm tốt hơn mức trung bình. Mức độ trừu tượng đó thay đổi theo công cụ bạn đang sử dụng. Nhận xét đi kèm với thói quen ngôn ngữ lắp ráp thường có mức độ "trừu tượng" thấp hơn, ví dụ, APL này A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕. Tôi nghĩ rằng có lẽ sẽ đáng để nhận xét về vấn đề mà nó dự định sẽ giải quyết, hmmm?


2

Nếu mã là tầm thường, nó không cần bình luận giải thích. Nếu mã là không tầm thường, bình luận giải thích rất có thể cũng sẽ không tầm thường.

Bây giờ, rắc rối với ngôn ngữ tự nhiên không tầm thường là nhiều người trong chúng ta không giỏi đọc hoặc viết nó. Tôi chắc rằng kỹ năng giao tiếp bằng văn bản của bạn là tuyệt vời, tuy nhiên một người nào đó ít nắm bắt ngôn ngữ viết có thể hiểu sai lời nói của bạn.

Nếu bạn rất cố gắng để viết ngôn ngữ tự nhiên không thể hiểu sai, bạn sẽ kết thúc bằng một thứ giống như một tài liệu pháp lý (và như tất cả chúng ta đều biết những thứ đó dài dòng và khó hiểu hơn mã).

Mã phải là mô tả ngắn gọn nhất về logic của bạn và không nên tranh luận nhiều về ý nghĩa của mã bởi vì trình biên dịch và nền tảng của bạn có tiếng nói cuối cùng.

Cá nhân tôi sẽ không nói rằng bạn không bao giờ nên viết bình luận. Chỉ có điều bạn nên xem xét lý do tại sao mã của bạn cần một nhận xét và cách bạn có thể khắc phục điều đó. Đây dường như là một chủ đề phổ biến trong câu trả lời ở đây.


Chính xác những gì tôi đã nghĩ khi tôi không đồng ý với tuyên bố "Một con người có thể hiểu một đoạn tiếng Anh nhanh hơn nhiều mà anh ta / cô ta có thể hiểu một đoạn mã có cùng ý nghĩa (miễn là hoạt động không tầm thường)" Mã là luôn ít mơ hồ và súc tích hơn.
stephenbayer

0

Một điểm chưa được đề cập là đôi khi bình luận chính xác những gì một đoạn mã có thể hữu ích trong trường hợp ngôn ngữ sử dụng một cú pháp cụ thể cho nhiều mục đích. Ví dụ: giả sử tất cả các biến là loại float, hãy xem xét:

f1 = (float)(f2+f3); // Force result to be rounded to single precision
f4 = f1-f2;

Hiệu quả của việc đúc một cách rõ ràng floatđể floatlà để buộc kết quả được làm tròn đến độ chính xác duy nhất; bình luận do đó có thể được xem như chỉ đơn giản là nói những gì mã làm. Mặt khác, so sánh mã đó với:

thing.someFloatProperty = (float)(f2*0.1); // Divide by ten

Ở đây, mục đích của dàn diễn viên là ngăn không cho trình biên dịch chạy theo cách tính toán chính xác hiệu quả nhất (f2 / 10) [chính xác hơn là nhân với 0,1f và trên hầu hết các máy, nó nhanh hơn chia cho 10,0f].

Nếu không có bình luận, một người nào đó đang xem lại mã cũ có thể nghĩ rằng dàn diễn viên đã được thêm vào với một niềm tin sai lầm rằng nó sẽ là cần thiết để ngăn chặn trình biên dịch không được sử dụng và điều đó là không cần thiết. Trên thực tế, dàn diễn viên phục vụ mục đích thực hiện chính xác những gì thông số kỹ thuật nói: làm cho kết quả tính toán được làm tròn thành độ chính xác đơn ngay cả trên các máy mà việc làm tròn sẽ tốn kém hơn so với việc giữ kết quả ở độ chính xác cao hơn. Cho rằng một dàn diễn viên floatcó thể có một số ý nghĩa và mục đích khác nhau, việc có một nhận xét xác định ý nghĩa nào được dự định trong một kịch bản cụ thể có thể giúp làm rõ rằng ý nghĩa thực tế phù hợp với ý định.


Tôi không chắc chắn rằng J. Lập trình viên ngẫu nhiên, nhìn vào ví dụ thứ hai, sẽ nhận ra rằng hằng số được viết 0,1 vì một lý do chính đáng, thay vì bởi vì lập trình viên ban đầu quên gõ 'f'.
David K

Đặc biệt trong quá trình gỡ lỗi, bạn không bao giờ cho rằng bất cứ điều gì đã được thực hiện vì một lý do tốt.
gnasher729

@DavidK: Mục đích của mã ví dụ thứ hai của tôi là tương phản nó với đoạn mã đầu tiên. Trong đoạn mã thứ hai, ý định của lập trình viên có lẽ là someFloatPropertynắm giữ đại diện chính xác nhất về f2/10điều đó có thể; do đó, mục đích chính của dàn diễn viên thứ hai đơn giản là để biên dịch mã . Tuy nhiên, trong ví dụ đầu tiên, việc truyền rõ ràng là không cần thiết cho mục đích thông thường của nó (thay đổi một loại thời gian biên dịch sang loại khác) vì các toán hạng đã có sẵn float. Nhận xét phục vụ để làm rõ rằng các diễn viên cần thiết cho một mục đích phụ (làm tròn).
supercat

Tôi đồng ý với quan điểm rằng bạn không cần đưa ra bất kỳ bình luận nào về (float)dàn diễn viên trong ví dụ thứ hai. Câu hỏi là về hằng số theo nghĩa đen 0.1. Bạn đã giải thích (trong đoạn văn bản tiếp theo) tại sao chúng tôi sẽ viết 0.1: "nó chính xác hơn nhân với 0,1f." Tôi đang đề nghị rằng đó là những từ nên có trong bình luận.
David K

@DavidK: Tôi chắc chắn sẽ bao gồm nhận xét nếu tôi biết rằng 0,1f sẽ không chính xác và sẽ sử dụng 0,1f nếu tôi biết rằng việc mất độ chính xác sẽ được chấp nhận và trên thực tế 0,1f sẽ nhanh hơn 0,1 . Nếu tôi không biết một trong những điều đó là đúng, thì thói quen mã hóa của tôi sẽ là sử dụng doublecho các hằng số hoặc các phép tính trung gian mà giá trị của nó có thể không thể biểu thị được như float[mặc dù trong các ngôn ngữ yêu cầu các diễn viên hai mặt rõ ràng khó chịu, sự lười biếng có thể thúc đẩy sử dụng các floathằng số không phải vì tốc độ, nhưng để giảm thiểu sự khó chịu].
supercat

-1

Nhận xét giải thích những gì mã làm là một hình thức sao chép. Nếu bạn thay đổi mã và sau đó quên cập nhật các bình luận, điều này có thể gây nhầm lẫn. Tôi không nói không sử dụng chúng, chỉ cần sử dụng chúng một cách thận trọng. Tôi đăng ký câu châm ngôn của chú Bob: "Chỉ nhận xét những gì mã không thể nói".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.