Bối cảnh
Gần đây tôi đã quan tâm đến việc sản xuất mã định dạng tốt hơn. Và tốt hơn tôi có nghĩa là "các quy tắc sau được chứng thực bởi đủ người để coi đó là một thực tiễn tốt" (vì dĩ nhiên sẽ không bao giờ có một cách mã "tốt nhất" duy nhất).
Ngày nay, tôi chủ yếu viết mã bằng Ruby nên tôi bắt đầu sử dụng một kẻ nói dối (Rubocop) để cung cấp cho tôi một số thông tin về "chất lượng" của mã của tôi ("chất lượng" này được định nghĩa bởi hướng dẫn kiểu ruby theo dự án do cộng đồng điều khiển ).
Lưu ý rằng tôi sẽ sử dụng "chất lượng" như trong "chất lượng của các định dạng", không quá nhiều về hiệu quả của mã này, ngay cả khi trong một số trường hợp, mã hiệu quả thực sự đang bị ảnh hưởng bởi cách mã được viết.
Dù sao, làm tất cả những điều đó, tôi nhận ra (hoặc ít nhất, đã nhớ) một vài điều:
- Một số ngôn ngữ (đáng chú ý nhất là Python, Ruby và như vậy) cho phép tạo ra một lớp mã tuyệt vời
- Thực hiện theo một số nguyên tắc cho mã của bạn có thể làm cho nó ngắn hơn đáng kể nhưng vẫn rất rõ ràng
- Tuy nhiên, làm theo các hướng dẫn này quá nghiêm ngặt có thể làm cho mã ít rõ ràng / dễ đọc hơn
- Mã có thể tôn trọng một số nguyên tắc gần như hoàn hảo và vẫn có chất lượng kém
- Khả năng đọc mã chủ yếu là chủ quan (như trong "những gì tôi thấy rõ ràng có thể hoàn toàn tối nghĩa với một nhà phát triển đồng nghiệp")
Đó chỉ là những quan sát, không phải là quy tắc tuyệt đối tất nhiên. Bạn cũng sẽ lưu ý rằng khả năng đọc mã và làm theo hướng dẫn có thể không liên quan tại thời điểm này nhưng ở đây hướng dẫn là một cách để thu hẹp số cách viết lại một đoạn mã.
Bây giờ, một số ví dụ, để làm cho tất cả rõ ràng hơn.
Ví dụ
Chúng ta hãy sử dụng một trường hợp sử dụng đơn giản: chúng ta có một ứng dụng với User
mô hình "". Một người sử dụng có tùy chọn firstname
và surname
và bắt buộc email
địa chỉ.
Tôi muốn viết một phương thức " name
" sẽ trả về sau đó là tên ( firstname + surname
) của người dùng nếu ít nhất là của anh ta firstname
hoặc surname
hiện tại, hoặc nó email
là một giá trị dự phòng nếu không.
Tôi cũng muốn phương thức này lấy " use_email
" làm tham số (boolean), cho phép sử dụng email người dùng làm giá trị dự phòng. "Đây use_email
" tham số nên mặc định (nếu không được thông qua) là " true
".
Cách đơn giản nhất để viết điều đó, trong Ruby, sẽ là:
def name(use_email = true)
# If firstname and surname are both blank (empty string or undefined)
# and we can use the email...
if (firstname.blank? && surname.blank?) && use_email
# ... then, return the email
return email
else
# ... else, concatenate the firstname and surname...
name = "#{firstname} #{surname}"
# ... and return the result striped from leading and trailing spaces
return name.strip
end
end
Mã này là cách đơn giản và dễ hiểu nhất để làm điều đó. Ngay cả đối với người không "nói" Ruby.
Bây giờ chúng ta hãy cố gắng làm cho nó ngắn hơn:
def name(use_email = true)
# 'if' condition is used as a guard clause instead of a conditional block
return email if (firstname.blank? && surname.blank?) && use_email
# Use of 'return' makes 'else' useless anyway
name = "#{firstname} #{surname}"
return name.strip
end
Điều này ngắn hơn, vẫn dễ hiểu, nếu không dễ hơn (mệnh đề bảo vệ là tự nhiên để đọc hơn một khối có điều kiện). Điều khoản bảo vệ cũng làm cho nó phù hợp hơn với các nguyên tắc tôi đang sử dụng, vì vậy win-win tại đây. Chúng tôi cũng giảm mức độ thụt lề.
Bây giờ chúng ta hãy sử dụng một số phép thuật Ruby để làm cho nó ngắn hơn nữa:
def name(use_email = true)
return email if (firstname.blank? && surname.blank?) && use_email
# Ruby can return the last called value, making 'return' useless
# and we can apply strip directly to our string, no need to store it
"#{firstname} #{surname}".strip
end
Thậm chí ngắn hơn và làm theo các hướng dẫn một cách hoàn hảo ... nhưng ít rõ ràng hơn vì việc thiếu tuyên bố trả lại làm cho nó hơi khó hiểu cho những người không quen với thực hành này.
Ở đây chúng ta có thể bắt đầu đặt câu hỏi: nó có thực sự đáng không? Chúng ta có nên nói "không, làm cho nó dễ đọc và thêm ' return
'" (biết điều này sẽ không tôn trọng các nguyên tắc). Hay chúng ta nên nói "Không sao đâu, đó là cách của Ruby, học ngôn ngữ chết tiệt!"?
Nếu chúng ta chọn tùy chọn B, thì tại sao không làm cho nó thậm chí ngắn hơn:
def name(use_email = true)
(email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end
Đây là, một lót! Tất nhiên là ngắn hơn ... ở đây chúng tôi tận dụng thực tế là Ruby sẽ trả về một giá trị hoặc giá trị khác tùy thuộc vào giá trị nào được xác định (vì email sẽ được xác định trong cùng điều kiện như trước).
Chúng tôi cũng có thể viết nó:
def name(use_email = true)
(email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end
Nó ngắn, không khó đọc (ý tôi là, tất cả chúng ta đều đã thấy một lớp lót xấu xí có thể trông như thế nào), Ruby tốt, nó tuân thủ hướng dẫn tôi sử dụng ... Nhưng vẫn vậy, so với cách viết đầu tiên nó, nó rất ít dễ đọc và dễ hiểu. Chúng tôi cũng có thể lập luận rằng dòng này quá dài (hơn 80 ký tự).
Câu hỏi
Một số ví dụ về mã có thể cho thấy rằng việc chọn giữa mã "kích thước đầy đủ" và nhiều phiên bản rút gọn của nó (xuống một lớp lót nổi tiếng) có thể khó khăn vì, như chúng ta có thể thấy, một lớp lót có thể không đáng sợ nhưng Tuy nhiên, không có gì có thể đánh bại mã "kích thước đầy đủ" về khả năng đọc ...
Vì vậy, đây là câu hỏi thực sự: dừng lại ở đâu? Khi nào ngắn, đủ ngắn? Làm thế nào để biết khi nào mã trở nên "quá ngắn" và ít đọc hơn (hãy nhớ rằng nó khá chủ quan)? Và hơn thế nữa: làm thế nào để luôn mã phù hợp và tránh trộn lẫn một lớp với các đoạn mã "kích thước đầy đủ" khi tôi cảm thấy thích nó?
TL; DR
Câu hỏi chính ở đây là: khi phải lựa chọn giữa một "đoạn mã dài nhưng rõ ràng, dễ đọc và dễ hiểu" và "mạnh mẽ, ngắn hơn nhưng khó đọc / khó hiểu hơn", biết hai cái đó là đầu và dưới cùng của một tỷ lệ và không phải là hai tùy chọn duy nhất: làm thế nào để xác định đâu là ranh giới giữa "đủ rõ ràng" và "không rõ ràng như cần phải có"?
Câu hỏi chính không phải là câu hỏi cổ điển "Một lớp lót so với khả năng đọc: cái nào tốt hơn?" nhưng "Làm thế nào để tìm sự cân bằng giữa hai điều đó?"
Chỉnh sửa 1
Nhận xét trong các ví dụ mã có nghĩa là "bỏ qua", chúng ở đây để làm rõ những gì đang xảy ra, nhưng không được tính đến khi đánh giá mức độ dễ đọc của mã.
return
từ khóa được thêm vào . Bảy nhân vật đó thêm vào một chút rõ ràng trong mắt tôi.
[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip
... bởi vì false.blank?
trả về đúng và toán tử tạm thời tiết kiệm cho bạn một vài ký tự ... \ _ () _ /
return
từ khóa nào cần thêm sự rõ ràng ?! Nó không cung cấp thông tin gì . Đó là sự lộn xộn thuần túy.