Nhận xét mã sạch so với tài liệu lớp


83

Tôi đang có một số cuộc thảo luận với các đồng nghiệp mới của tôi về việc bình luận. Cả hai chúng tôi đều thích Clean Code , và tôi hoàn toàn ổn với thực tế là nên tránh các bình luận mã nội tuyến và tên lớp và phương thức nên được sử dụng để thể hiện những gì họ làm.

Tuy nhiên, tôi là một fan hâm mộ lớn của việc thêm các tóm tắt lớp nhỏ cố gắng giải thích mục đích của lớp và những gì thực sự đại diện, chủ yếu để dễ dàng duy trì mô hình nguyên tắc trách nhiệm duy nhất . Tôi cũng đã quen với việc thêm các tóm tắt một dòng vào các phương thức giải thích phương thức này phải làm là gì. Một ví dụ điển hình là phương pháp đơn giản

public Product GetById(int productId) {...}

Tôi đang thêm tóm tắt phương pháp sau

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary

Tôi tin rằng thực tế là phương thức trả về null nên được ghi lại. Một nhà phát triển muốn gọi một phương thức không cần phải mở mã của tôi để xem liệu phương thức đó có trả về null hay ném ngoại lệ hay không. Đôi khi, nó là một phần của giao diện, vì vậy nhà phát triển thậm chí không biết mã cơ bản nào đang chạy?

Tuy nhiên, các đồng nghiệp của tôi nghĩ rằng những loại bình luận này là " mùi mã " và "bình luận luôn là thất bại" ( Robert C. Martin ).

Có cách nào để diễn đạt và truyền đạt các loại kiến ​​thức này mà không cần thêm ý kiến? Vì tôi là một fan hâm mộ lớn của Robert C. Martin, tôi có một chút bối rối. Là tóm tắt giống như ý kiến ​​và do đó luôn luôn thất bại?

Đây không phải là một câu hỏi về ý kiến ​​nội tuyến.


38
Robert Martin nói "Bình luận luôn là thất bại"? Chà, sau đó anh ta là một kẻ cực đoan, và nên uống một nhúm muối. (Vâng, tôi biết rằng ông viết như thế này cho các mục đích tu từ, để có được thông điệp của mình qua điểm của tôi là,. Vì vậy nên bạn .)
Kilian Foth

18
Những cuốn sách của chú Bob nên đi kèm với một túi muối 1kg ...
AK_

6
Nếu bạn đang theo dõi Robert Martin, tài liệu cho trường hợp null nên là một bài kiểm tra. Đó là, bạn nên có một bài kiểm tra cho thấy trong trường hợp nào phương thức có thể trả về null. Ngoài ra, vì đây là Java, nên một chú thích @Nullable cũng sẽ tốt hơn bình luận.
Martin Epsz

15
@Bjorn Tôi sở hữu một bản sao của Clean Code và đã đọc nó để che đi nhiều lần. Vâng, chú Bob thích mã để tự viết tài liệu, nhưng có nhiều ví dụ về nhận xét trong mã riêng của mình trong cuốn sách . Vấn đề là nếu bạn cảm thấy bắt buộc phải viết bình luận, hãy thật sự cố gắng thay đổi mã thay vì thêm bình luận, nhưng không được phép bình luận hoàn toàn (ngay cả bình luận nội tuyến).

6
Phương thức nên được gọi là TryGetById và bình luận nên được xóa.
usr

Câu trả lời:


116

Như những người khác đã nói, có một sự khác biệt giữa nhận xét tài liệu API và nhận xét nội tuyến. Từ quan điểm của tôi, sự khác biệt chính là một bình luận nội tuyến được đọc cùng với , trong khi một bình luận tài liệu được đọc cùng với chữ ký của bất cứ điều gì bạn đang bình luận.

Với điều này, chúng ta có thể áp dụng nguyên tắc DRY tương tự . Là bình luận nói giống như chữ ký? Hãy xem ví dụ của bạn:

Lấy một sản phẩm theo id của nó

Phần này chỉ lặp lại những gì chúng ta đã thấy từ tên GetByIdcộng với kiểu trả về Product. Nó cũng đặt ra câu hỏi về sự khác biệt giữa "nhận" và "truy xuất" là gì, và mã mang so với nhận xét có gì khác biệt. Vì vậy, nó không cần thiết và hơi khó hiểu. Nếu bất cứ điều gì, nó đang cản trở phần thực sự hữu ích, phần thứ hai của bình luận:

trả về null nếu không tìm thấy sản phẩm.

Ah! Đó là điều mà chúng tôi chắc chắn không thể biết chắc chắn chỉ từ chữ ký và cung cấp thông tin hữu ích.


Bây giờ thực hiện điều này một bước xa hơn. Khi mọi người nói về các bình luận khi mã có mùi, câu hỏi không phải là mã cần bình luận hay không, nhưng liệu bình luận đó có cho thấy mã có thể được viết tốt hơn hay không, để thể hiện thông tin trong bình luận. Đó là "mùi mã" nghĩa là gì - nó không có nghĩa là "không làm điều này!", Nó có nghĩa là "nếu bạn đang làm điều này, nó có thể là một dấu hiệu có vấn đề".

Vì vậy, nếu đồng nghiệp của bạn nói với bạn nhận xét này về null là mùi mã, bạn chỉ cần hỏi họ: "Được rồi, tôi nên diễn đạt điều này như thế nào?" Nếu họ có câu trả lời khả thi, bạn đã học được điều gì đó. Nếu không, nó có thể sẽ giết chết những lời phàn nàn của họ.


Liên quan đến trường hợp cụ thể này, nhìn chung vấn đề null được biết đến là một vấn đề khó khăn. Có một lý do cơ sở mã được đặt rải rác với các điều khoản bảo vệ, tại sao kiểm tra null là điều kiện tiên quyết phổ biến cho các hợp đồng mã, tại sao sự tồn tại của null được gọi là "sai lầm tỷ đô". Không có nhiều lựa chọn khả thi. Tuy nhiên, một thứ phổ biến được tìm thấy trong C # là Try...quy ước:

public bool TryGetById(int productId, out Product product);

Trong các ngôn ngữ khác, có thể là thành ngữ khi sử dụng một loại (thường được gọi là một cái gì đó giống như Optionalhoặc Maybe) để chỉ ra một kết quả có thể có hoặc không có ở đó:

public Optional<Product> GetById(int productId);

Vì vậy, theo một cách nào đó, lập trường chống bình luận này đã đưa chúng ta đến một nơi nào đó: ít nhất chúng ta đã nghĩ về việc liệu bình luận này có đại diện cho mùi hay không, và những gì thay thế có thể tồn tại cho chúng ta.

Việc chúng ta có thực sự thích những thứ này hơn chữ ký gốc hay không là một cuộc tranh luận khác, nhưng ít nhất chúng ta có các tùy chọn để thể hiện thông qua mã hơn là nhận xét những gì xảy ra khi không tìm thấy sản phẩm. Bạn nên thảo luận với các đồng nghiệp của mình về những lựa chọn nào trong số những lựa chọn này mà họ cho là tốt hơn và tại sao, và hy vọng sẽ giúp tiến xa hơn những tuyên bố giáo điều về những bình luận.


9
Hoặc Linqtương đương của Try..., ...OrDefaultsẽ trả về default(T)nếu mệnh đề sẽ dẫn đến một kết quả trống.
Bart van Nierop

4
Tôi thực sự đánh giá cao sự khác biệt của bạn giữa nhận xét mã nội tuyến và nhận xét tài liệu và các ví dụ đã cho :)
Rachel

2
Các giá trị trả về có thể có của một hàm nên được hiển thị bằng chữ ký của nó. Các TryGetValuemô hình là một cách hợp lý để làm điều này trong C #, nhưng ngôn ngữ chức năng nhất có một cách tốt hơn để đại diện cho một giá trị thiếu. Đọc thêm tại đây
AlexFoxGill

2
@BenAaronson: Nếu muốn có một giao diện chung có thể hỗ trợ hiệp phương sai, người ta có thể sử dụng T TryGetValue(params, ref bool success)cho bất kỳ loại T nào, hoặc T TryGetValue(params), với null cho thấy thất bại, đối với loại T bị ràng buộc lớp, nhưng TryGetXXmẫu trả về boolkhông tương thích với hiệp phương sai.
supercat

6
Trong Java 8, bạn có thể trả về một Optional<Product>dấu hiệu cho biết có thể không có Sản phẩm nào được trả về từ phương thức.
Wim Deblauwe

102

Các trích dẫn Robert C. Martin được đưa ra khỏi bối cảnh. Đây là trích dẫn với một chút bối cảnh:

Không có gì có thể khá hữu ích như một bình luận được đặt tốt. Không có gì có thể làm lộn xộn một mô-đun nhiều hơn những bình luận giáo điều phù phiếm. Không có gì có thể gây tổn hại lớn như một bình luận cũ nát mà tuyên truyền dối trá và thông tin sai lệch.

Nhận xét không giống như Danh sách của Schindler. Họ không phải là "tốt thuần túy." Thật vậy, bình luận, tốt nhất, là một điều ác cần thiết. Nếu ngôn ngữ lập trình của chúng tôi đủ biểu cảm hoặc nếu chúng tôi có tài năng khéo léo sử dụng các ngôn ngữ đó để thể hiện ý định của mình, chúng tôi sẽ không cần bình luận nhiều - có lẽ không phải vậy.

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ã. Lưu ý rằng tôi đã sử dụng từ thất bại. Ý tôi là thế Bình luận 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.

Vì vậy, khi bạn thấy mình ở một vị trí mà bạn cần viết bình luận, hãy suy nghĩ kỹ và xem liệu không có cách nào để xoay bảng và thể hiện bản thân bằng mã. Mỗi khi bạn thể hiện bản thân bằng mã, bạn nên vỗ nhẹ vào lưng. Mỗi khi bạn viết bình luận, bạn nên nhăn nhó và cảm nhận sự thất bại trong khả năng diễn đạt của mình.

(Sao chép từ đây , nhưng trích dẫn ban đầu là từ Clean Code: A Handbook of Agile Software Crafts )

Làm thế nào trích dẫn này được giảm thành "Bình luận luôn luôn thất bại" là một ví dụ tốt về cách một số người sẽ đưa ra một trích dẫn hợp lý ra khỏi bối cảnh và biến nó thành giáo điều ngu ngốc.


Tài liệu API (như javadoc) được cho là tài liệu API để người dùng có thể sử dụng nó mà không cần phải đọc mã nguồn . Vì vậy, trong trường hợp này, tài liệu nên giải thích những gì phương pháp làm. Bây giờ bạn có thể lập luận rằng "Lấy một sản phẩm theo id của nó" là không cần thiết bởi vì nó đã được chỉ định bởi tên phương thức, nhưng thông tin nullcó thể được trả về chắc chắn rất quan trọng đối với tài liệu, vì điều này không rõ ràng.

Nếu bạn muốn tránh sự cần thiết của nhận xét, bạn phải loại bỏ vấn đề tiềm ẩn (đó là việc sử dụng nulllàm giá trị trả về hợp lệ), bằng cách làm cho API rõ ràng hơn. Ví dụ: bạn có thể trả về một số loại Option<Product>, vì vậy chữ ký loại tự truyền đạt rõ ràng những gì sẽ được trả lại trong trường hợp không tìm thấy sản phẩm.

Nhưng trong mọi trường hợp, việc ghi lại API hoàn toàn chỉ thông qua tên phương thức và chữ ký loại là không thực tế. Sử dụng nhận xét tài liệu cho bất kỳ thông tin không rõ ràng bổ sung nào mà người dùng nên biết. Hãy nói rằng tài liệu API từ DateTime.AddMonths()trong BCL:

Phương thức AddMonths tính toán tháng và năm kết quả, có tính đến năm nhuận và số ngày trong một tháng, sau đó điều chỉnh phần ngày của đối tượng DateTime kết quả. Nếu ngày kết quả không phải là ngày hợp lệ trong tháng kết quả, ngày hợp lệ cuối cùng của tháng kết quả sẽ được sử dụng. Ví dụ: ngày 31 tháng 3 + 1 tháng = ngày 30 tháng 4. Phần thời gian trong ngày của đối tượng DateTime kết quả vẫn giống như trường hợp này.

Không có cách nào bạn có thể diễn tả điều này bằng cách chỉ sử dụng tên phương thức và chữ ký! Tất nhiên tài liệu lớp học của bạn có thể không yêu cầu mức độ chi tiết này, nó chỉ là một ví dụ.


Bình luận nội tuyến cũng không tệ.

Xấu comments là xấu. Ví dụ các bình luận chỉ giải thích những gì có thể nhìn thấy một cách tầm thường từ mã, ví dụ cổ điển là:

// increment x by one
x++;

Nhận xét giải thích điều gì đó có thể được làm rõ bằng cách đổi tên một biến hoặc phương thức hoặc cơ cấu lại mã, là một mùi mã:

// data1 is the collection of tasks which failed during execution
var data1 = getData1();

Đây là những loại bình luận Martin chống lại. Nhận xét là một triệu chứng của việc không viết mã rõ ràng - trong trường hợp này là sử dụng tên tự giải thích cho các biến và phương thức. Bản thân bình luận tất nhiên không phải là vấn đề, vấn đề là chúng ta cần bình luận để hiểu mã.

Nhưng các bình luận nên được sử dụng để giải thích mọi thứ không rõ ràng từ mã, ví dụ tại sao mã được viết theo một cách không rõ ràng nhất định:

// need to reset foo before calling bar due to a bug in the foo component.
foo.reset()
foo.bar();

Các bình luận giải thích những gì một đoạn mã quá phức tạp cũng là một mùi, nhưng cách khắc phục không phải là ngoài ý kiến, sửa lỗi là sửa mã! Nói một cách thực tế, mã phức tạp đã xảy ra (hy vọng chỉ là tạm thời cho đến khi tái cấu trúc) nhưng không có nhà phát triển bình thường nào viết mã sạch hoàn hảo ngay lần đầu tiên. Khi mã phức tạp xảy ra, tốt hơn là viết bình luận giải thích những gì nó làm hơn là không viết bình luận. Nhận xét này cũng sẽ làm cho nó dễ dàng hơn để tái cấu trúc sau này.

Đôi khi mã là không thể tránh khỏi phức tạp. Nó có thể là một thuật toán phức tạp, hoặc nó có thể là mã hy sinh sự rõ ràng vì lý do hiệu suất. Một lần nữa ý kiến ​​là cần thiết.


13
Ngoài ra còn có trường hợp mã của bạn xử lý một tình huống mà chỉ phức tạp, và không có mã đơn giản có thể xử lý nó.
gnasher729

6
Điểm tốt, gnasher. Điều này dường như thường xảy ra khi bạn phải tối ưu hóa một số đoạn mã cho hiệu suất.
JacquesB

9
Ngay cả một nhận xét trong x++cũng có thể tốt nếu đó là một cái gì đó như "tăng x thêm một, bao quanh nếu đó là UINT32_MAX"; Bất cứ ai biết thông số kỹ thuật ngôn ngữ đều biết rằng việc tăng uint32_tgói sẽ, nhưng không có nhận xét, người ta có thể không biết liệu gói đó có phải là một phần dự kiến của thuật toán được thực hiện hay không.
supercat

5
@ l0b0: Tôi hy vọng bạn đang nói đùa!
JacquesB

9
@ l0b0 Không có thứ gọi là mã tạm thời. Mã không bao giờ được tái cấu trúc vì doanh nghiệp hài lòng với kết quả và sẽ không chấp thuận tài trợ để sửa nó. Năm năm nữa, một số nhà phát triển cơ sở sẽ thấy mã này, bạn thậm chí không sử dụng WizBug 4.0 nữa vì bạn đã thay thế nó bằng Bugtrocity v9, vì vậy "Bug123" không có nghĩa gì với anh ta. Bây giờ anh ấy nghĩ rằng đây là cách mà mã vĩnh viễn được coi là, và tiến tới là một nhà phát triển khủng khiếp trong toàn bộ sự nghiệp của anh ấy. Hãy nghĩ về những đứa trẻ. Đừng viết mã tạm thời.
corsiKa

36

Có một sự khác biệt giữa nhận xét mã của bạnghi lại mã của bạn .

  • Nhận xét là cần thiết để duy trì mã sau này, đó là thay đổi chính mã.

    Nhận xét thực sự có thể được coi là có vấn đề. Các điểm cực đoan sẽ nói rằng họ luôn luôn chỉ ra một vấn đề, hoặc là bên trong mã của bạn (mã quá khó hiểu) hoặc trong vòng ngôn ngữ (ngôn ngữ không thể đủ biểu cảm, ví dụ, thực tế là phương pháp không bao giờ quay trở lại, nullcó thể được thể hiện thông qua các hợp đồng Mã trong C #, nhưng không có cách nào để thể hiện nó thông qua mã trong, giả sử, PHP).

  • Tài liệu là cần thiết để có thể sử dụng các đối tượng (lớp, giao diện) bạn đã phát triển. Đối tượng mục tiêu là khác nhau: đó không phải là những người sẽ duy trì mã của bạn và thay đổi mã mà chúng ta đang nói ở đây, mà là những người hầu như không cần sử dụng nó.

    Xóa tài liệu vì mã đủ rõ ràng là điên rồ, vì tài liệu ở đây đặc biệt để có thể sử dụng các lớp và giao diện mà không phải đọc hàng ngàn dòng mã .


Đúng, nhưng ít nhất một phần quan điểm của Martin là với thực tiễn phát triển hiện đại, các bài kiểm tra là tài liệu chứ không phải bản thân mã. Giả sử mã đang được thử nghiệm với hệ thống kiểm tra kiểu BDD, ví dụ như dòng chảy, bản thân các thử nghiệm là mô tả trực tiếp có thể đọc được về hành vi của phương thức ("được cung cấp cơ sở dữ liệu về sản phẩm khi GetById được gọi với id của sản phẩm hợp lệ. đối tượng Sản phẩm phù hợp được trả về [...] khi GetById được gọi với id sản phẩm không hợp lệ thì null được trả về "hoặc đại loại như thế này).
Jules

13

Chà, có vẻ như đồng nghiệp của bạn đọc sách, tiếp thu những gì họ nói và áp dụng những gì anh ta học được mà không cần suy nghĩ và không có bất kỳ cân nhắc nào về bối cảnh.

Nhận xét của bạn về chức năng nên làm gì để bạn có thể vứt bỏ mã thực thi, tôi đọc bình luận của hàm và tôi có thể viết thay thế cho việc thực hiện, mà không có gì sai.

Nếu bình luận không cho tôi biết liệu một ngoại lệ được ném ra hay liệu có được trả lại không, tôi không thể làm điều đó. Hơn nữa, nếu nhận xét không cho bạn biết liệu một ngoại lệ được ném ra hay liệu nil có được trả về hay không, thì bất cứ nơi nào bạn gọi hàm, bạn phải đảm bảo mã của bạn hoạt động chính xác cho dù một ngoại lệ được ném hay không được trả về.

Vì vậy, đồng nghiệp của bạn là hoàn toàn sai. Và đi trước và đọc tất cả các cuốn sách, nhưng suy nghĩ cho chính mình.

Tái bút Tôi thấy dòng của bạn "đôi khi nó là một phần của giao diện, vì vậy bạn thậm chí không biết mã nào đang chạy." Ngay cả với các chức năng ảo, bạn cũng không biết mã nào đang chạy. Tệ hơn, nếu bạn đã viết một lớp trừu tượng, thậm chí không có bất kỳ mã nào! Vì vậy, nếu bạn có một lớp trừu tượng với một hàm trừu tượng, các ý kiến ​​mà bạn thêm vào hàm trừu tượng là điều duy nhất mà một người triển khai của một lớp cụ thể phải hướng dẫn chúng. Những nhận xét đó cũng có thể là điều duy nhất có thể hướng dẫn người dùng của lớp, ví dụ nếu tất cả những gì bạn có là một lớp trừu tượng và một nhà máy trả lại một triển khai cụ thể, nhưng bạn không bao giờ thấy bất kỳ mã nguồn nào của việc triển khai. (Và tất nhiên tôi không nên xem mã nguồn của một lần thực hiện).


Tôi đã không nhận xét mã trong 10 năm. Bình luận là phình to, rác rưởi. Không ai bình luận mã những ngày này. Chúng tôi tập trung vào mã được hình thành và đặt tên tốt, các mô-đun nhỏ, tách rời, v.v ... RATNG làm cho mã của bạn có thể đọc được không phải là nhận xét. Các xét nghiệm đảm bảo rằng nếu bạn vứt bỏ mã, không có gì sai, không bình luận. Các thử nghiệm cho bạn biết cách bạn sử dụng mã bạn viết, cách bạn gọi chúng và tại sao chúng ở đó ngay từ đầu. Bạn đang ở trường quá cũ, bạn cần phải học về kiểm tra và làm sạch mã của bạn tôi.
positiveGuy

12

Có hai loại ý kiến ​​để xem xét - những loại hiển thị cho những người có mã và những người sử dụng để tạo tài liệu.

Loại bình luận mà chú Bob đang đề cập đến là loại chỉ hiển thị cho những người có mã. Những gì anh ấy đang ủng hộ là một hình thức DRY . Đối với một người đang xem mã nguồn, mã nguồn phải là tài liệu mà họ cần. Ngay cả trong trường hợp mọi người có quyền truy cập vào mã nguồn, các bình luận không phải lúc nào cũng xấu. Đôi khi, các thuật toán rất phức tạp hoặc bạn cần nắm bắt lý do tại sao bạn thực hiện một cách tiếp cận không rõ ràng để người khác không phá vỡ mã của bạn nếu họ cố sửa lỗi hoặc thêm một tính năng mới.

Các ý kiến ​​mà bạn đang mô tả là tài liệu API. Đây là những thứ hiển thị cho những người sử dụng triển khai của bạn, nhưng có thể không có quyền truy cập vào mã nguồn của bạn. Ngay cả khi họ có quyền truy cập vào mã nguồn của bạn, họ có thể đang làm việc trên các mô-đun khác và không nhìn vào mã nguồn của bạn. Những người này sẽ thấy hữu ích khi có tài liệu này có sẵn trong IDE của họ khi họ đang viết mã.


Thành thật tôi không bao giờ nghĩ rằng DRY áp dụng cho mã + bình luận, nhưng nó có ý nghĩa hoàn hảo. Sắp xếp giống như ví dụ "tăng X" trong câu trả lời của @ JacquesB.

7

Giá trị của một bình luận được đo bằng giá trị của thông tin mà nó mang lại trừ đi nỗ lực cần thiết để đọc nó và / hoặc bỏ qua nó. Vì vậy, nếu chúng tôi phân tích nhận xét

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary>

về giá trị và chi phí, chúng ta thấy ba điều:

  1. Retrieves a product by its idlặp lại những gì tên của hàm nói, vì vậy nó có giá mà không có giá trị. Nó sẽ bị xóa.

  2. returns null if no product was foundlà thông tin rất có giá trị. Nó có khả năng làm giảm thời gian các lập trình viên khác sẽ phải xem xét việc thực hiện chức năng. Tôi khá chắc chắn rằng nó tiết kiệm đọc nhiều hơn chi phí đọc mà nó tự giới thiệu. Nó nên ở lại.

  3. Những dòng kẻ

    /// <summary>
    /// </summary>
    

    không mang thông tin gì. Họ là chi phí thuần túy cho người đọc bình luận. Chúng có thể hợp lý nếu trình tạo tài liệu của bạn cần chúng nhưng trong trường hợp đó có lẽ bạn nên nghĩ về một trình tạo tài liệu khác.

    Đây là lý do tại sao việc sử dụng các trình tạo tài liệu là một ý tưởng không thể tranh cãi: Chúng thường yêu cầu rất nhiều ý kiến ​​bổ sung không mang thông tin hoặc lặp lại nội dung rõ ràng, chỉ vì một tài liệu được đánh bóng.


Một quan sát mà tôi không tìm thấy trong bất kỳ câu trả lời nào khác:

Ngay cả những bình luận không cần thiết để hiểu / sử dụng mã cũng có thể rất có giá trị. Đây là một ví dụ như vậy:

//XXX: The obvious way to do this would have been ...
//     However, since we need this functionality primarily for ...
//     doing this the non-obvious way of ...
//     gives us the advantage of ...

Có thể rất nhiều văn bản, hoàn toàn không cần thiết để hiểu / sử dụng mã. Tuy nhiên, nó giải thích một lý do tại sao mã trông giống như nó. Nó sẽ khiến mọi người dừng lại khi nhìn vào mã, tự hỏi tại sao nó không được thực hiện theo cách rõ ràng và sẽ bắt đầu tái cấu trúc mã cho đến khi họ nhận ra lý do tại sao mã được viết như thế này ngay từ đầu. Và ngay cả khi người đọc đủ thông minh để không nhảy vào tái cấu trúc trực tiếp, họ vẫn cần tìm hiểu tại sao mã lại trông giống như vậy trước khi họ nhận ra rằng tốt nhất nên giữ nguyên như vậy. Nhận xét này theo nghĩa đen có thể tiết kiệm giờ làm việc. Do đó giá trị cao hơn chi phí.

Tương tự như vậy, các bình luận có thể truyền đạt ý định của một mã, thay vì chỉ cách nó hoạt động. Và họ có thể vẽ bức tranh lớn thường bị mất trong chi tiết nhỏ của chính mã. Như vậy, bạn có quyền được bình luận ủng hộ lớp. Tôi đánh giá cao nhất các bình luận của lớp nếu họ giải thích ý định của lớp, cách nó tương tác với các lớp khác, nó được sử dụng như thế nào, v.v ... Than ôi, tôi không phải là tác giả lớn của những bình luận đó ...


2
Wow - thay đổi trình tạo tài liệu của bạn vì nó yêu cầu thêm một vài dòng html để phân tích cú pháp? Đừng mà.
corsiKa

2
@corsiKa YMMV, nhưng tôi vì một người sẽ thích một trình tạo tài liệu giúp giảm chi phí trong các bình luận xuống mức tối thiểu. Tất nhiên, tôi cũng muốn đọc một tệp tiêu đề được viết tốt hơn là một tài liệu doxygen không đồng bộ với mã thực tế. Nhưng, như tôi đã nói, YMMV.
cmaster

4
Ngay cả khi tên của một phương thức mô tả mục đích của nó một cách xuất sắc, việc lặp lại mục đích đó bằng ngôn ngữ tự nhiên có thể giúp ai đó đọc nó dễ dàng hơn để liên kết mục đích đó với bất kỳ cảnh báo nào tiếp theo. Một sự phân chia lại những gì được mô tả trong tên sẽ đủ ngắn gọn để ngay cả khi giá trị thấp, chi phí cũng sẽ thấp. Do đó tôi không đồng ý với phần đầu tiên của bài viết của bạn. +1, tuy nhiên, cho phần thứ hai. Tài liệu về các phương pháp thay thế đã được đánh giá và từ chối có thể cực kỳ có giá trị, nhưng thông tin đó hiếm khi được chú ý.
supercat

GetByIdđặt ra câu hỏi, id là gì, và cũng có thể nhận được những gì từ đâu. Nhận xét tài liệu nên cho phép môi trường phát triển hiển thị câu trả lời cho những câu hỏi này. Trừ khi nó được giải thích ở nơi khác trong các nhận xét tài liệu mô-đun, nó cũng sẽ là một nơi để cho biết lý do tại sao một người sẽ nhận được id.
hyde

Nhận xét thổi, mã sạch (tự mô tả), TDD (nhận phản hồi thường xuyên và nhận phản hồi về thiết kế của bạn thường xuyên) và các quy tắc kiểm tra (cung cấp cho bạn sự tự tin và hành vi tài liệu)! KIỂM TRA người KIỂM TRA. Không ai ở đây đang nói về điều đó. thức dậy
Tích cực

4

Mã không ghi chú là mã xấu. Đó là một huyền thoại phổ biến (nếu không phải là phổ quát) rằng mã có thể được đọc theo nhiều cách giống như, nói, tiếng Anh. Nó phải được giải thích, và đối với bất kỳ nhưng mã tầm thường nhất cần có thời gian và công sức. Thêm vào đó, mọi người đều có một mức độ khả năng khác nhau trong cả đọc và viết bất kỳ ngôn ngữ nào. Sự khác biệt giữa phong cách và khả năng mã hóa của tác giả là những rào cản mạnh mẽ đối với việc giải thích chính xác. Nó cũng là một huyền thoại mà bạn có thể rút ra ý định của tác giả từ việc thực hiện mã. Theo kinh nghiệm của tôi, hiếm khi sai thêm ý kiến ​​thêm.

Robert Martin và cộng sự. coi đó là một "mùi hôi" bởi vì có thể là mã được thay đổi và các ý kiến ​​thì không. Tôi nói rằng đó là một điều tốt (theo cách chính xác giống như một "mùi hôi" được thêm vào khí đốt trong nhà để cảnh báo người dùng rò rỉ). Đọc bình luận cung cấp cho bạn một điểm khởi đầu hữu ích để diễn giải mã thực tế. Nếu chúng khớp thì bạn sẽ tăng độ tin cậy vào mã. Nếu chúng khác nhau thì bạn đã phát hiện ra mùi cảnh báo và cần điều tra thêm. Cách chữa "mùi hôi" không phải là loại bỏ mùi mà là bịt kín chỗ rò rỉ.


2
Cho rằng bạn là một tác giả thông minh và văn bản là vì lợi ích của một đối tượng thông minh; Bạn vẽ đường bằng cách sử dụng thông thường. Rõ ràng, như hiện tại, ví dụ này thật ngu ngốc nhưng điều đó không có nghĩa là có thể không có lý do chính đáng để làm rõ với một nhận xét tại sao mã bao gồm i ++ tại thời điểm đó.
Vince O'Sullivan

8
i++; // Adjust for leap years.
Vince O'Sullivan

5
"Robert Martin và cộng sự coi đó là một" mùi hôi "bởi vì có thể mã được thay đổi và các ý kiến ​​thì không." Đó chỉ là một phần của mùi. Điều tồi tệ nhất của mùi xuất phát từ ý tưởng rằng lập trình viên quyết định không thử viết mã theo cách mô tả hơn, và thay vào đó chọn cách "niêm phong rò rỉ" bằng một bình luận. Khẳng định của ông là thay vì đưa ra một nhận xét "// điều chỉnh cho năm nhuận", có lẽ người ta nên có một phương thức "điều chỉnhForLeapYears ()" trong chính mã (hoặc một cái gì đó tương tự). Tài liệu này xuất hiện dưới dạng các bài kiểm tra thực hiện logic năm nhuận.
Eric King

3
Tôi sẽ xem xét việc thêm một lệnh gọi phương thức để thay thế một dòng mã bằng một ghi chú quá mức, đặc biệt vì tên của phương thức thực sự chỉ là một nhận xét gắn nhãn một đoạn mã và không đảm bảo độ chính xác của tài liệu tốt hơn. (Nếu dòng đó xảy ra ở hai hoặc nhiều nơi thì việc giới thiệu một cuộc gọi phương thức, tất nhiên, sẽ chính xác là giải pháp phù hợp.)
Vince O'Sullivan

1
@Jay Lý do là làm cho sự trừu tượng của bạn rõ ràng (bởi, ví dụ, giới thiệu các phương thức) quy tắc. Không làm như vậy bởi vì bạn có thể kết thúc bằng một phương thức có một dòng là ngoại lệ. Hãy để tôi diễn giải quy tắc tùy ý thực sự : "Sử dụng cấu trúc của ngôn ngữ lập trình của bạn (thường là các phương thức và lớp) để giới thiệu trừu tượng, trừ khi việc thực hiện trừu tượng hóa đó có thể được biểu thị dưới dạng một dòng mã, trong trường hợp đó chỉ định sự trừu tượng hóa bằng ngôn ngữ tự nhiên bằng cách thêm một lời bình luận."
Eric

3

Trong một số ngôn ngữ (ví dụ F #), toàn bộ nhận xét / tài liệu này thực sự có thể được thể hiện bằng chữ ký phương thức. Điều này là do trong F #, null thường không phải là giá trị được phép trừ khi được cho phép cụ thể.

Điều phổ biến trong F # (và một số ngôn ngữ chức năng khác) là thay vì null, bạn sử dụng một loại tùy chọn Option<T>có thể là Nonehoặc Some(T). Ngôn ngữ sau đó sẽ hiểu điều này và buộc bạn (hoặc cảnh báo bạn nếu bạn không) khớp với cả hai trường hợp khi bạn cố gắng sử dụng nó.

Vì vậy, ví dụ trong F # bạn có thể có một chữ ký trông như thế này

val GetProductById: int -> Option<Product>

Và sau đó, đây sẽ là một hàm lấy một tham số (int) và sau đó trả về một sản phẩm hoặc giá trị Không có.

Và sau đó bạn có thể sử dụng nó như thế này

let product = GetProduct 42
match product with
| None -> printfn "No product found!"
| Some p -> DoThingWithProduct p

Và bạn sẽ nhận được cảnh báo trình biên dịch nếu bạn không khớp cả hai trường hợp có thể. Vì vậy, không có cách nào khả thi để có các ngoại lệ tham chiếu null ở đó (trừ khi bạn bỏ qua các cảnh báo của trình biên dịch) và bạn biết mọi thứ bạn cần chỉ bằng cách nhìn vào chữ ký hàm.

Tất nhiên, điều này đòi hỏi ngôn ngữ của bạn được thiết kế theo cách này - điều mà nhiều ngôn ngữ phổ biến như C #, Java, C ++, v.v. Vì vậy, điều này có thể không hữu ích cho bạn trong tình huống hiện tại của bạn. Nhưng thật tuyệt (hy vọng) thật tuyệt khi biết rằng có những ngôn ngữ ngoài kia cho phép bạn thể hiện loại thông tin này theo cách gõ tĩnh mà không cần dùng đến bình luận, v.v. :)


1

Có một số câu trả lời xuất sắc ở đây và tôi không muốn nhắc lại những gì họ nói. Nhưng hãy để tôi thêm một vài bình luận. (Không có ý định chơi chữ.)

Có rất nhiều tuyên bố mà những người thông minh đưa ra - về phát triển phần mềm và nhiều chủ đề khác, tôi cho rằng - đó là lời khuyên rất tốt khi hiểu trong ngữ cảnh, nhưng những người ngớ ngẩn hơn là đưa ra khỏi bối cảnh hoặc áp dụng trong các tình huống không phù hợp hoặc đưa vào cực kỳ lố bịch.

Ý tưởng rằng mã nên được tự ghi lại là một ý tưởng tuyệt vời như vậy. Nhưng trong cuộc sống thực, có những hạn chế về tính thực tiễn của ý tưởng này.

Một lưu ý là ngôn ngữ có thể không cung cấp các tính năng để ghi lại những gì cần được ghi lại rõ ràng và chính xác. Khi ngôn ngữ máy tính được cải thiện, điều này ngày càng trở thành một vấn đề. Nhưng tôi không nghĩ rằng nó đã hoàn toàn biến mất. Quay trở lại những ngày khi tôi viết bằng trình biên dịch, nó rất có ý nghĩa khi đưa vào một nhận xét như "tổng giá = giá của các mặt hàng không chịu thuế cộng với giá của các mặt hàng chịu thuế cộng với giá của các mặt hàng chịu thuế * thuế suất". Chính xác những gì đã có trong một đăng ký tại bất kỳ thời điểm nào không nhất thiết phải rõ ràng. Phải mất nhiều bước để thực hiện một thao tác đơn giản. V.v. Nhưng nếu bạn đang viết bằng một ngôn ngữ hiện đại, một nhận xét như vậy sẽ chỉ là sự phân chia lại một dòng mã.

Tôi luôn cảm thấy khó chịu khi thấy những bình luận như "x = x + 7; // thêm 7 vào x". Giống như, wow, cảm ơn, nếu tôi đã quên những gì một dấu cộng có nghĩa là có thể rất hữu ích. Nơi tôi có thể thực sự bối rối là khi biết "x" là gì hoặc tại sao lại bắt buộc phải thêm 7 vào lúc này. Mã này có thể được tự tạo tài liệu bằng cách đặt tên có ý nghĩa hơn cho "x" và sử dụng hằng số ký hiệu thay vì 7. Giống như nếu bạn đã viết "Total_price = Total_price + MEMBERSHIP_FEE;", thì có thể không cần bình luận .

Nghe có vẻ hay khi nói rằng một tên hàm sẽ cho bạn biết chính xác chức năng đó làm gì để mọi bình luận bổ sung sẽ bị thừa. Tôi có những kỷ niệm đẹp về thời gian tôi viết một hàm kiểm tra xem một số mục có trong bảng Mục cơ sở dữ liệu của chúng tôi không, trả về đúng hay sai và tôi gọi là "ValidateItemNumber". Có vẻ như một cái tên đàng hoàng. Sau đó, một người khác đã đến và sửa đổi chức năng đó để tạo một đơn đặt hàng cho mặt hàng và cập nhật cơ sở dữ liệu, nhưng không bao giờ thay đổi tên. Bây giờ tên rất sai lệch. Nghe có vẻ như nó đã làm một việc nhỏ, khi thực sự nó đã làm nhiều hơn thế. Sau đó, một người nào đó thậm chí đã lấy ra một phần về việc xác nhận số vật phẩm và làm điều đó ở một nơi khác, nhưng vẫn không thay đổi tên. Vì vậy, mặc dù chức năng bây giờ không liên quan gì đến việc xác thực số mục,

Nhưng trong thực tế, tên hàm thường không thể mô tả hoàn toàn chức năng làm gì nếu không có tên của hàm miễn là mã tạo nên hàm đó. Là tên sẽ cho chúng ta biết chính xác những gì xác nhận được thực hiện trên tất cả các tham số? Điều gì xảy ra trong điều kiện ngoại lệ? Đánh vần mọi sự mơ hồ có thể? Tại một số điểm, tên sẽ trở nên dài đến nỗi nó trở nên khó hiểu. Tôi chấp nhận String BuildFullName (String Firstname, String Lastname) làm chữ ký hàm phong nha. Mặc dù điều đó không đánh vần cho dù tên được thể hiện là "đầu tiên cuối cùng" hay "cuối cùng, đầu tiên" hoặc một số biến thể khác, nó sẽ làm gì nếu một hoặc cả hai phần của tên trống, nếu nó áp đặt các giới hạn về độ dài kết hợp và Nó làm gì nếu vượt quá,

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.