Một lớp so với khả năng đọc: khi nào nên ngừng giảm mã? [đóng cửa]


14

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 Usermô hình "". Một người sử dụng có tùy chọn firstnamesurnamevà 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 firstnamehoặc surnamehiện tại, hoặc nó emaillà 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ã.


7
Quá ngắn cho một câu trả lời: tiếp tục tái cấu trúc lặp đi lặp lại cho đến khi bạn không chắc nó tốt hơn lần lặp trước đó rồi dừng lại và đảo ngược lần tái cấu trúc cuối cùng.
Dom

8
Tôi thích biến thể 3 với returntừ 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.
cmaster - phục hồi monica

2
Nếu bạn cảm thấy thực sự khủng khiếp, bạn có thể viết toàn bộ là [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ự ... \ _ () _ /
DaveMongoose

1
OK, tôi phải hỏi: returntừ 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.
Konrad Rudolph

2
Quan niệm cho rằng sự ngắn gọn bắt đầu sự rõ ràng không chỉ chịu sự điều chỉnh của lợi nhuận giảm dần, mà còn đảo ngược khi bị đẩy đến cực đoan. Nếu bạn đang viết lại để làm cho một chức năng ngắn ngắn hơn, bạn đang lãng phí thời gian của mình, và điều tương tự cũng xảy ra khi cố gắng biện minh cho việc thực hành.
sdenham

Câu trả lời:


26

Bất kể mã nào bạn viết, có thể đọc là tốt nhất. Ngắn là tốt thứ hai. Và có thể đọc được thường có nghĩa là đủ ngắn để bạn có thể hiểu mã, các định danh được đặt tên tốt và tuân thủ các thành ngữ phổ biến của ngôn ngữ mà mã được viết.

Nếu đây là bất khả tri về ngôn ngữ, tôi nghĩ rằng điều này chắc chắn sẽ dựa trên quan điểm, nhưng trong giới hạn của ngôn ngữ Ruby, tôi nghĩ chúng ta có thể trả lời nó.

Đầu tiên, một tính năng và cách viết thành ngữ của Ruby là bỏ qua returntừ khóa khi trả về một giá trị, trừ khi trả về sớm từ một phương thức.

Một tính năng và thành ngữ khác được kết hợp là sử dụng các ifcâu lệnh trailing để tăng khả năng đọc mã. Một trong những ý tưởng lái xe trong Ruby là viết mã đọc như ngôn ngữ tự nhiên. Đối với điều này, chúng tôi đi đến Hướng dẫn sâu sắc của _why về Ruby, Chương 3 .

Đọc to những điều sau đây cho chính mình.

5.times { print "Odelay!" }

Trong câu tiếng Anh, dấu câu (như dấu chấm, dấu chấm than, dấu ngoặc đơn) là im lặng. Dấu câu thêm ý nghĩa cho các từ, giúp đưa ra tín hiệu như những gì tác giả dự định bằng một câu. Vì vậy, hãy đọc những điều trên như: Năm lần in ấn Odelay!

Vì điều này, ví dụ mã số 3 là thành ngữ nhất đối với Ruby:

def name(use_email = true)
  return email if firstname.blank? && surname.blank? && use_email

  "#{firstname} #{surname}".strip
end

Bây giờ khi chúng ta đọc mã, nó nói:

Trả lại e-mail nếu tên trống và họ trống và sử dụng e-mail

(trở lại) tên và họ bị tước

Đó là khá gần với mã Ruby thực tế.

Nó chỉ có 2 dòng mã thực tế, vì vậy nó khá ngắn gọn và nó tuân thủ các thành ngữ của ngôn ngữ.


Điểm hay. Đúng là câu hỏi không có nghĩa là Ruby-centric, nhưng tôi đồng ý rằng không thể có câu trả lời bất khả tri về ngôn ngữ ở đây.
Sudiukil

8
Tôi thấy ý tưởng làm cho mã nghe giống như ngôn ngữ tự nhiên được đánh giá quá cao (và đôi khi thậm chí có vấn đề). Nhưng ngay cả khi không có động lực này, tôi đi đến kết luận giống như câu trả lời này.
Konrad Rudolph

1
Có một điều chỉnh nữa tôi sẽ xem xét làm mã. Đó là, đặt use_emailtrước các điều kiện khác vì đó là biến thay vì gọi hàm. Nhưng sau đó một lần nữa nội suy chuỗi lại thay đổi sự khác biệt.
John Dvorak

Mã cấu trúc theo cấu trúc ngôn ngữ tự nhiên có thể khiến bạn rơi vào bẫy ngôn ngữ. Ví dụ: khi bạn đọc các yêu cầu tiếp theo do send an email if A, B, C but no D, theo tiền đề của bạn sẽ là tự nhiên để gõ xuống 2 khối if / khác , khi đó có lẽ sẽ dễ mã hơn if not D, send an email. Hãy cẩn thận tại thời điểm đọc ngôn ngữ tự nhiên và chuyển đổi nó thành mã bởi vì nó có thể khiến bạn viết một phiên bản mới của "Câu chuyện không bao giờ" . Với các lớp, phương thức và biến. Rốt cuộc không phải là một vấn đề lớn
Laiv

@Laiv: Làm cho mã đọc như ngôn ngữ tự nhiên không có nghĩa là dịch các yêu cầu theo nghĩa đen. Nó có nghĩa là viết mã để khi đọc thành tiếng, nó cho phép người đọc hiểu logic mà không cần đọc từng bit mã, ký tự cho ký tự, cấu trúc ngôn ngữ để xây dựng ngôn ngữ. Nếu mã hóa thì if !Dtốt hơn, đó là Dmột cái tên có ý nghĩa. Và nếu !toán tử bị mất giữa các mã khác, thì việc có một mã định danh được gọi NotDlà phù hợp.
Greg Burghardt

15

Tôi không nghĩ bạn sẽ nhận được câu trả lời tốt hơn là "sử dụng phán đoán tốt nhất của bạn". Tóm lại bạn nên cố gắng cho sự rõ ràng hơn là ngắn gọn . Thông thường, mã ngắn nhất cũng là rõ ràng nhất, nhưng nếu bạn chỉ tập trung vào việc đạt được sự rõ ràng ngắn có thể bị ảnh hưởng. Đây rõ ràng là trường hợp trong hai ví dụ cuối cùng, đòi hỏi nhiều nỗ lực để hiểu hơn ba ví dụ trước.

Một xem xét quan trọng là khán giả của mã. Khả năng đọc tất nhiên phụ thuộc hoàn toàn vào người đọc. Những người bạn mong đợi đọc mã (bên cạnh chính mình) có thực sự biết thành ngữ của ngôn ngữ Ruby không? Vâng câu hỏi này không phải là điều mà mọi người ngẫu nhiên trên internet có thể trả lời, đây chỉ là quyết định của riêng bạn.


Tôi đồng ý với quan điểm của khán giả, nhưng đó là một phần trong cuộc đấu tranh của tôi: vì phần mềm của tôi thường là nguồn mở, khán giả có thể được sáng tác bởi những người mới bắt đầu cũng như "các vị thần Ruby". Tôi có thể giữ cho nó đơn giản để làm cho nó có thể truy cập được với hầu hết mọi người, nhưng nó cảm thấy như một sự lãng phí những lợi thế mà ngôn ngữ mang lại.
Sudiukil

1
Là một người đã phải tiếp quản, mở rộng và duy trì một số mã thực sự khủng khiếp, sự rõ ràng phải giành chiến thắng. Hãy nhớ câu ngạn ngữ cũ - Viết mã của bạn như thể người bảo trì là Thiên thần địa ngục đầy thù hận, người biết bạn sống ở đâu và con bạn đi học ở đâu.
uɐɪ

2
@Sudiukil: Đó là một điểm quan trọng. Tôi đề nghị bạn phấn đấu cho mã thành ngữ trong trường hợp đó (nghĩa là có kiến ​​thức tốt về ngôn ngữ), vì dù sao người mới bắt đầu cũng không thể đóng góp cho mã nguồn mở. (Hoặc nếu có, họ sẽ sẵn sàng nỗ lực học ngôn ngữ.)
JacquesB

7

Một phần của vấn đề ở đây là "khả năng đọc" là gì. Đối với tôi, tôi nhìn vào ví dụ mã đầu tiên của bạn:

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

Và tôi cảm thấy khó đọc vì nó chứa đầy những bình luận "ồn ào" chỉ lặp lại mã. Loại bỏ chúng:

def name(use_email = true)
 if (firstname.blank? && surname.blank?) && use_email
  return email
 else
  name = "#{firstname} #{surname}"
  return name.strip
 end
end

và bây giờ nó dễ đọc hơn nhiều. Khi đọc nó, tôi nghĩ "hmm, tôi tự hỏi liệu Ruby có hỗ trợ toán tử ternary không? Trong C #, tôi có thể viết nó dưới dạng:

string Name(bool useEmail = true) => 
    firstName.Blank() && surname.Blank() && useEmail 
    ? email 
    : $"{firstname} {surname}".Strip();

Là một cái gì đó như thế có thể trong ruby? Làm việc qua bài viết của bạn, tôi thấy có:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end

Tất cả những thứ tốt. Nhưng điều đó không thể đọc được đối với tôi; đơn giản vì tôi phải cuộn để xem toàn bộ dòng. Vì vậy, hãy sửa nó:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) 
 || "#{firstname} #{surname}".strip
end

Bây giơ tôi thây hạnh phuc. Tôi không hoàn toàn chắc chắn về cách thức hoạt động của cú pháp, nhưng tôi có thể hiểu mã làm gì.

Nhưng đó chỉ là tôi. Dân gian khác có những ý tưởng rất khác nhau về những gì làm cho một đoạn mã đẹp để đọc. Vì vậy, bạn cần biết đối tượng của mình khi viết mã. Nếu bạn là người mới bắt đầu giảng dạy tuyệt đối, thì bạn sẽ muốn đơn giản và có thể viết nó như ví dụ đầu tiên của bạn. Nếu bạn làm việc giữa một nhóm các nhà phát triển chuyên nghiệp có nhiều năm kinh nghiệm về ruby, thì hãy viết mã tận dụng ngôn ngữ và giữ cho nó ngắn gọn. Nếu nó ở đâu đó ở giữa, thì hãy nhắm đến một nơi nào đó ở giữa.

Một điều tôi sẽ nói mặc dù: hãy cẩn thận "mã thông minh", chẳng hạn như trong ví dụ cuối cùng của bạn. Hãy tự hỏi mình, liệu có [firstname, surname].all?(&:blank?)thêm bất cứ điều gì ngoài việc khiến bạn cảm thấy thông minh bởi vì nó thể hiện kỹ năng của bạn, ngay cả khi bây giờ khó đọc hơn một chút? Tôi muốn nói ví dụ này có khả năng đầy đủ vào danh mục đó. Nếu bạn đang so sánh năm giá trị, tôi sẽ xem đó là một mã tốt. Vì vậy, một lần nữa, không có dòng tuyệt đối ở đây, chỉ cần chú ý là quá thông minh.

Vì vậy, tóm lại: khả năng đọc yêu cầu bạn biết đối tượng của mình và nhắm mục tiêu mã của bạn phù hợp và viết ngắn gọn, nhưng mã rõ ràng; không bao giờ viết mã "thông minh". Giữ nó ngắn, nhưng không quá ngắn.


2
Chà, tôi đã quên đề cập đến nó, nhưng các bình luận có nghĩa là "bị bỏ qua", chúng ở đây chỉ để giúp những người không biết rõ về Ruby. Điểm hợp lệ mặc dù về khán giả, tôi đã không nghĩ về điều đó. Đối với phiên bản khiến bạn hài lòng: nếu đó là độ dài dòng quan trọng, phiên bản thứ ba của mã của tôi (phiên bản chỉ có một câu lệnh trả về) có làm được điều đó và thậm chí còn dễ hiểu hơn một chút không?
Sudiukil

1
@Sudiukil, không phải là một nhà phát triển ruby, tôi thấy rằng khó đọc nhất và nó không phù hợp với những gì tôi đang tìm kiếm (từ quan điểm của ngôn ngữ khác) là giải pháp "tốt nhất". Tuy nhiên, đối với một người quen thuộc với thực tế rằng ruby ​​là một trong những ngôn ngữ trả về giá trị của biểu thức cuối cùng, nó có thể đại diện cho phiên bản đơn giản nhất, dễ đọc nhất. Một lần nữa, đó là tất cả về khán giả của bạn.
David Arno

Không phải là nhà phát triển Ruby nhưng điều này có ý nghĩa với tôi hơn nhiều so với câu trả lời được bình chọn hàng đầu, có nội dung như "Đây là những gì tôi sẽ trả lại [chú thích: trong một điều kiện dài cụ thể]. Ngoài ra, đây là một chuỗi đến muộn đến bữa tiệc. " Logic về cơ bản chỉ là một tuyên bố trường hợp nên được viết giống như một tuyên bố trường hợp thống nhất, không lan truyền trên nhiều tuyên bố dường như không liên quan.
Paul

Cá nhân tôi sẽ đi với khối mã thứ hai của bạn ngoại trừ tôi sẽ kết hợp hai câu lệnh trong nhánh khác của bạn thành một:return "#{firstname} #{surname}".strip
Paul

2

Đây có lẽ là một câu hỏi mà thật khó để không đưa ra câu trả lời dựa trên ý kiến, nhưng, đây là hai xu của tôi.

Nếu bạn thấy rằng làm cho mã ngắn hơn không ảnh hưởng đến khả năng đọc, hoặc thậm chí cải thiện mã, hãy tìm mã. Nếu mã trở nên ít đọc hơn, thì bạn cần xem xét nếu có một lý do khá chính đáng để bỏ nó theo cách đó. Làm điều đó chỉ vì nó ngắn hơn, hoặc mát mẻ, hoặc chỉ vì bạn có thể, là những ví dụ về lý do xấu. Bạn cũng cần xem xét nếu làm cho mã ngắn hơn sẽ làm cho những người khác mà bạn làm việc cùng dễ hiểu hơn.

Vì vậy, những gì sẽ là một lý do tốt? Đó thực sự là một cuộc gọi phán xét, nhưng một ví dụ có thể giống như tối ưu hóa hiệu suất (tất nhiên, sau khi thử nghiệm hiệu năng, tất nhiên, không phải trước). Một cái gì đó mang lại cho bạn một số lợi ích mà bạn sẵn sàng trả cho việc giảm khả năng đọc. Trong trường hợp đó, bạn có thể giảm thiểu nhược điểm bằng cách cung cấp một nhận xét hữu ích (giải thích mã đó làm gì và tại sao nó phải được tạo ra một chút khó hiểu). Thậm chí tốt hơn, bạn có thể trích xuất mã đó thành một hàm riêng biệt với một tên có ý nghĩa, để nó chỉ là một dòng tại trang web cuộc gọi giải thích những gì đang xảy ra (thông qua tên của chức năng) mà không đi sâu vào chi tiết (tuy nhiên, mọi người có sự khác biệt ý kiến ​​về điều này, vì vậy đây là một cuộc gọi phán xét khác mà bạn phải đưa ra).


1

Câu trả lời là một chút chủ quan, nhưng bạn phải tự hỏi mình với tất cả sự trung thực mà bạn có thể tập hợp được, nếu bạn có thể hiểu được mã đó khi bạn quay lại nó trong một hoặc hai tháng.

Mỗi thay đổi sẽ cải thiện khả năng hiểu mã của người bình thường. Để làm cho mã dễ hiểu, nó giúp sử dụng các nguyên tắc sau:

  • Tôn trọng các thành ngữ của ngôn ngữ . C #, Java, Ruby, Python đều có những cách ưa thích để làm điều tương tự. Các cấu trúc thành ngữ giúp hiểu mã mà bạn không quen thuộc.
  • Dừng lại khi mã của bạn trở nên ít đọc hơn . Trong ví dụ bạn cung cấp, điều đó đã xảy ra khi bạn nhấn các cặp mã giảm cuối cùng. Bạn đã đánh mất lợi thế thành ngữ của ví dụ trước và đưa ra rất nhiều biểu tượng đòi hỏi nhiều suy nghĩ để thực sự hiểu những gì đang diễn ra.
  • Chỉ sử dụng ý kiến ​​khi bạn phải biện minh cho điều gì đó bất ngờ . Tôi biết các ví dụ của bạn đã ở đó để giải thích các cấu trúc cho những người ít quen thuộc với Ruby và điều đó không sao cho một câu hỏi. Tôi thích sử dụng các bình luận để giải thích các quy tắc kinh doanh bất ngờ và tránh chúng nếu mã có thể tự nói lên.

Điều đó nói rằng, có những lúc mã mở rộng giúp hiểu những gì đang diễn ra tốt hơn. Một ví dụ với điều đó đến từ C # và LINQ. LINQ là một công cụ tuyệt vời và có thể tăng cường khả năng đọc trong một số tình huống, nhưng tôi cũng gặp phải một số tình huống khó hiểu hơn nhiều. Tôi đã có một số phản hồi trong đánh giá ngang hàng đề nghị biến biểu thức thành một vòng lặp với các câu lệnh thích hợp để người khác có thể duy trì nó tốt hơn. Khi tôi tuân thủ, họ đã đúng. Về mặt kỹ thuật, LINQ có nhiều thành ngữ hơn cho C #, nhưng có những trường hợp nó làm giảm sự dễ hiểu và một giải pháp dài dòng hơn sẽ cải thiện nó.

Tôi nói tất cả những gì để nói điều này:

Cải thiện khi bạn có thể làm cho mã của mình tốt hơn (dễ hiểu hơn)

Hãy nhớ rằng, bạn hoặc ai đó như bạn sẽ phải duy trì mã đó sau. Lần tiếp theo bạn đi qua có thể là vài tháng nữa. Làm cho mình một ưu tiên và không đuổi theo việc giảm số lượng dòng với chi phí để có thể hiểu mã của bạn.


0

Khả năng đọc là một tài sản bạn muốn có, có nhiều-một-lớp không. Vì vậy, thay vì "một lớp so với khả năng đọc", câu hỏi nên là:

Khi nào thì một lớp lót tăng khả năng đọc, và khi nào chúng làm hại nó?

Tôi tin rằng một lớp lót tốt cho khả năng đọc khi chúng đáp ứng hai điều kiện sau:

  1. Chúng quá cụ thể để được trích xuất thành một chức năng.
  2. Bạn không muốn làm gián đoạn "dòng chảy" của việc đọc mã xung quanh.

Ví dụ: giả sử namekhông phải là một tên ... tốt cho phương pháp của bạn. Việc kết hợp tên và họ, hoặc sử dụng email thay vì tên, không phải là điều tự nhiên phải làm. Vì vậy, thay vì nameđiều tốt nhất bạn có thể nghĩ ra đã trở nên dài và cồng kềnh:

puts "Name: #{user.email_if_there_is_no_name_otherwise_use_firstname_and_surname(use_email)}"

Tên dài như vậy chỉ ra rằng điều này rất cụ thể - nếu nó chung chung hơn, bạn có thể đã tìm thấy một tên chung hơn. Vì vậy, gói nó trong một phương thức không giúp ích gì cho việc không đọc được (quá dài) cũng như DRYness (quá cụ thể để sử dụng ở bất kỳ nơi nào khác), vì vậy tốt hơn là chỉ để lại mã trong đó.

Tuy nhiên - tại sao làm cho nó một lót? Chúng thường ít đọc hơn mã multiline. Đây là nơi chúng ta nên kiểm tra điều kiện thứ hai của tôi - dòng mã xung quanh. Điều gì nếu bạn có một cái gì đó như thế này:

puts "Group: #{user.group}"
puts "Title: #{user.title}"
if user.firstname.blank? && user.surname.blank?) && use_email
  name = email
else
  name = "#{firstname} #{surname}"
  name.strip
end
puts "Name: #{name}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

Bản thân mã multiline có thể đọc được - nhưng khi bạn cố đọc mã xung quanh (in các trường khác nhau) mà cấu trúc multiline đang làm gián đoạn dòng chảy. Điều này dễ đọc hơn:

puts "Group: #{user.group}"
puts "Title: #{user.title}"
puts "Name: #{(email if (user.firstname.blank? && user.surname.blank?) && use_email) || "#{user.firstname} #{user.surname}".strip}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

Luồng của bạn không bị gián đoạn và bạn có thể tập trung vào biểu thức cụ thể nếu bạn cần.

Đây có phải là trường hợp của bạn? Chắc chắn không phải!

Điều kiện đầu tiên ít liên quan hơn - bạn đã coi nó đủ chung chung để xứng đáng với một phương thức và đã đưa ra một tên cho phương thức đó dễ đọc hơn nhiều so với triển khai. Rõ ràng là bạn sẽ không trích xuất nó vào một chức năng một lần nữa.

Đối với điều kiện thứ hai - nó có làm gián đoạn dòng chảy của mã xung quanh không? Không! Mã xung quanh là một khai báo phương thức, rằng chọn namenó là mục đích duy nhất của nó. Logic của việc chọn tên không làm gián đoạn dòng chảy của mã xung quanh - đó là mục đích của mã xung quanh!

Kết luận - không làm cho toàn bộ cơ thể chức năng trở thành một lớp lót

Một lớp lót là tốt khi bạn muốn làm một cái gì đó hơi phức tạp mà không làm gián đoạn dòng chảy. Một khai báo hàm đã làm gián đoạn dòng chảy (do đó nó sẽ không bị gián đoạn khi bạn gọi hàm đó), do đó, làm cho toàn bộ thân hàm là một lớp lót không giúp đọc được.

Ghi chú

Tôi đang đề cập đến "đầy đủ thổi" chức năng và phương pháp - chức năng không inline hoặc biểu thức lambda đó thường một phần của mã xung quanh và cần phải phù hợp với bên trong nó của dòng chảy.

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.