Mã của tôi nên DRY hoặc có thể đọc được nếu nó không thể là cả hai?


14

Tôi đang viết mã Ruby cho một bài tập mã hóa đơn giản và thường xuyên chạy qua tình huống khó xử này (bài tập là một mật mã solitaire nếu bạn phải biết). Đó là một câu hỏi về việc liệu tôi có nên đưa ra logic của mình bằng các biến mô tả và các câu lệnh một bước làm cho hàm có thể đọc được thay vì một câu lệnh ngắn gọn, thậm chí dày đặc loại bỏ sự lặp lại và / hoặc giảm thiểu các cơ hội cho lỗi.

Ví dụ gần đây nhất của tôi: Chương trình của tôi lấy đầu vào và do các nguyên tắc định dạng cứng nhắc, nó có thể dễ dàng xác định xem đầu vào nên được mã hóa hay giải mã. Để đơn giản hóa, một khi khóa mã hóa và tin nhắn được chuyển đổi / tạo ra để tương thích, vấn đề trừ khóa khỏi tin nhắn được mã hóa hoặc thêm khóa vào tin nhắn không được mã hóa, để có được đầu ra mong muốn (nghĩ về khóa là mã hóa, tin nhắn + mã hóa = mã; mã - mã hóa = tin nhắn). Vị trí DRY nói với tôi rằng tôi nên chuyển đổi tin nhắn được mã hóa khác với tin nhắn không được mã hóa của mình để chức năng lấy khóa mã hóa và áp dụng nó vào tin nhắn không bao giờ cần phân biệt. Tôi đã thấy rằng điều này có nghĩa là tôi cần một số câu lệnh lồng nhau trong hàm nhưng logic có vẻ ổn định. Mã này, tuy nhiên, không dễ đọc.

Mặt khác, tôi có thể viết hai hàm khác nhau được gọi dựa trên cờ được đặt khi ứng dụng xác định mã hóa hoặc giải mã. Điều này sẽ đơn giản hơn để đọc nhưng sẽ nhân đôi chức năng cấp cao của việc áp dụng khóa mã hóa cho tin nhắn (khiến nó được mã hóa hoặc giải mã).

Tôi nên nghiêng về mã có thể đọc được, hay mã ngắn gọn? Hoặc tôi đã bỏ lỡ một cách khác để có được chức năng này và đáp ứng cả hai nguyên tắc? Đây có phải là một vị trí dọc theo một quy mô trong đó người ta phải xem xét mục đích của dự án và đưa ra quyết định tốt nhất để phục vụ mục đích đó?

Cho đến nay, tôi có xu hướng nhấn mạnh mã súc tích, mã DRY hơn mã có thể đọc được.


2
Có thể sử dụng đầu tiên, DRY thứ hai, thứ ba có thể đọc được. Mã có thể sử dụng cao dẫn đến mã tiêu thụ rất dễ đọc, dễ gặp DRY và dễ đọc hơn. Đây là lý do tại sao bạn muốn có những phức tạp khó chịu và đóng gói chúng ở đâu đó với một API đẹp, nếu chúng không thể được cải thiện; ít nhất là mã tương tác với chúng sẽ không bị xấu cũng như cách tiếp cận này.
Jimmy Hoffa

4
Một số ví dụ mã thực tế có thể giúp ích, tôi nghi ngờ bạn đang thiếu một số cách thứ 3 như các hàm bậc cao hơn để có mã khô và dễ đọc
jk.

2
Không trùng lặp. Súc tích v. Có thể đọc và DRY v. Có thể đọc được là hai so sánh rất khác nhau. Không DRY nguy hiểm hơn nhiều so với không súc tích.
djechlin

Chỉ cần không rơi vào cái bẫy mà giả sử mã bị trùng lặp nhiều thì dễ đọc hơn vì nó rơi vào một mẫu có thể dự đoán được. Tôi nghi ngờ có thể dễ dàng nghĩ như vậy, cho đến khi bạn duy trì một hệ thống sao chép quá nhiều đến nỗi bạn tìm thấy các lỗi tinh vi ở một nhánh không thuộc nhánh khác, gần giống nhau.
KChaloux

Tôi không hiểu ý của bạn là gì bởi "chức năng cấp cao của việc áp dụng khóa mã hóa cho tin nhắn". Nếu bạn có nghĩa là có sự chồng chéo giữa mã hóa và giải mã, hãy xem xét sử dụng tái cấu trúc phương thức trích xuất để xác định các phần chung.
Aaron Kurtzhals

Câu trả lời:


20

DRY là một hướng dẫn, không phải là một tôn giáo. Những người đưa nó đến điểm DRY trên tất cả, đã đưa nó đi quá xa.

Đầu tiên và quan trọng nhất, mã có thể sử dụng là tối quan trọng. Nếu mã không hữu ích và có thể sử dụng được, thì đó không phải là ... và không có điểm nào để viết nó ở vị trí đầu tiên.

Thứ hai, một ngày nào đó, ai đó sẽ phải duy trì mã của bạn. Nếu mã của bạn không thể duy trì được, chúng sẽ phá vỡ thiết kế DRY dày đặc, "đẹp" của bạn trong khi nguyền rủa tên của bạn. Đừng làm điều này. Tôi đã từng là người đó và mỗi lần tôi nhìn thấy một cái tên nào đó trong chú thích mã, tôi lại rùng mình.

Tạo mã dày đặc thiếu "các biến mô tả" và dán mọi thứ vào các biểu thức ternary lồng nhau với lambdas mà không có bất kỳ tài liệu nào là thông minh. Thật tuyệt khi biết rằng bạn có thể làm điều đó - nhưng không. Mã thông minh là rất khó để gỡ lỗi. Tránh viết mã thông minh .

Hầu hết thời gian dành cho phần mềm được dành cho việc bảo trì phần mềm - không phải viết nó lần đầu tiên. Viết mã để bạn (hoặc người khác) có thể nhanh chóng và dễ dàng sửa lỗi và thêm các tính năng theo yêu cầu với ít thay đổi thiết kế lý tưởng.


Theo trực giác hay không, bạn đã gặp phải một vấn đề mà tôi đang xem xét. Tôi đã viết mã 'thông minh' hết mức có thể cho bài tập này, chỉ để biết tôi có thể làm được, điều mà tôi đồng ý là rất tốt để biết, nhưng tôi đồng ý với bạn ở đây rằng đó là một thực tiễn tồi. Bây giờ, tôi có rất nhiều tái cấu trúc để làm.
lutze

1
@lutze Tôi là một lập trình viên perl và đôi khi là ruby ​​và chắc chắn có thể hiểu được sự cám dỗ để viết mã thông minh trong đó. Một phiên bản trước của câu trả lời này bao gồm một trích dẫn của Larry Wall về sự kiêu ngạo - tôi tin rằng đây là một đức tính rất quan trọng khi làm việc với mã mà một ngày nào đó sẽ được duy trì. Do sự khó hiểu của các ngôn ngữ, đôi khi rất khó để có được lợi thế của việc viết nhiều mã hơn là cố gắng tìm mã dày đặc thông minh ... cho đến khi bạn phải duy trì nó.

17

Tôi không chắc chắn dựa trên câu hỏi mà bạn hiểu DRY. Mã DRY không giống như súc tích. Khá thường xuyên thì ngược lại.

Ở đây, tôi không chắc vấn đề lớn cho ví dụ này là gì. Tạo một chức năng để mã hóa, một chức năng để giải mã, trợ giúp cho chức năng chung (trộn byte) và một giao diện đơn giản để nhận đầu vào và xác định mã hóa / giải mã ... Không lặp lại chính mình.

Nhìn chung, cả DRY và khả năng đọc đều tồn tại để giúp duy trì và mở rộng mã. Không phải tất cả các kịch bản đều như nhau, một cú đánh dễ đọc lớn để loại bỏ một chút lặp lại là không tốt, và cũng không phải là một loạt các bản sao để thêm một chút khả năng đọc.

Nếu nhấn, tôi thích đọc hơn. Mã trùng lặp vẫn có thể được kiểm tra - mã không thể đọc được dẫn đến bạn làm (và kiểm tra) điều sai.


Điểm tuyệt vời, tôi thực sự đã kết hợp nguyên tắc DRY một chút với mục tiêu mã ngắn gọn.
lutze

12

Tôi không quen thuộc với trường hợp cụ thể của bạn, nhưng có thể đưa ra một số hướng dẫn chung.

Mục đích của cả khả năng đọc và DRY là khả năng duy trì .

Khả năng bảo trì rất quan trọng trong hầu hết các tình huống bạn sẽ dành nhiều thời gian để duy trì mã hơn là viết mã. Điều này đặc biệt đúng nếu bạn coi việc viết mã là một loại bảo trì đặc biệt.

Thật không may, DRY thường bị hiểu lầm. Điều này một phần vì nó có vẻ đơn giản, nhưng giống như nhiều thứ đơn giản có thể, cũng ... phức tạp.

Ý định od DRY là mọi đơn vị chức năng tồn tại ở một nơi duy nhất. Nếu nguyên tắc được tuân theo, người duy trì mã được giao nhiệm vụ thay đổi hoặc xác minh rằng chức năng có thể xử lý mã ngay lập tức. Nếu DRY không được theo dõi thì có một mối nguy hiểm rất thực tế là một số bản sao của chức năng sẽ không được duy trì đúng cách.

Vi phạm trắng trợn nhất của DRY là mã hóa sao chép, trong đó toàn bộ các khối mã được lặp lại nguyên văn trong cơ sở mã. Điều này là đáng ngạc nhiên phổ biến trong kinh nghiệm của tôi, và không có cách nào nó thêm vào khả năng đọc. Do đó, tái cấu trúc mã để giới thiệu một phương thức phổ biến luôn làm tăng tính tuân thủ và khả năng đọc của DRY.

Vi phạm trắng trợn thứ hai là "sao chép-dán và thay đổi một chút". Một lần nữa, tái cấu trúc để giới thiệu một phương thức comon với các tham số, hoặc chia một hàm thành các bước và trừu tượng hóa các điểm tương đồng hầu như luôn làm tăng khả năng đọc.

Sau đó, có những vi phạm tinh vi hơn, trong đó chức năng được nhân đôi nhưng mã là khác nhau. Điều này luôn không dễ dàng nhận ra, nhưng khi bạn nhìn thấy nó và tái cấu trúc để kéo mã chung vào một phương thức / lớp / hàm duy nhất thì mã đó sẽ dễ đọc hơn trước đây.

Cuối cùng, có những trường hợp lặp lại thiết kế. Ví dụ: bạn có thể đã sử dụng mẫu trạng thái nhiều lần trong cơ sở mã của mình và bạn đang xem xét tái cấu trúc để loại bỏ sự lặp lại này. Trong trường hợp này, tiến hành thận trọng. Bạn cũng có thể giảm khả năng đọc bằng cách giới thiệu nhiều mức độ trừu tượng hơn. Đồng thời, bạn không thực sự đối phó với sự trùng lặp của functiionality mà là sự trùng lặp của sự trừu tượng hóa. Đôi khi nó đáng giá ... nhưng thường thì không. Nguyên tắc hướng dẫn của bạn sẽ là hỏi, "cái nào trong số này dễ bảo trì hơn".

Bất cứ khi nào tôi thực hiện các cuộc gọi phán xét này, tôi cố gắng xem xét lượng thời gian mọi người sẽ dành để duy trì mã. Nếu mã là chức năng kinh doanh cốt lõi thì có lẽ nó sẽ cần bảo trì nhiều hơn. Trong trường hợp đó, tôi đặt mục tiêu làm cho mã có thể duy trì được cho những người quen thuộc với cơ sở mã và các tóm tắt liên quan. Tôi sẽ vui hơn khi giới thiệu một chút trừu tượng hơn để giảm sự lặp lại. Ngược lại, một tập lệnh hiếm khi được sử dụng và duy trì không thường xuyên có thể ít dễ nắm bắt hơn cho các nhà bảo trì nếu nó liên quan đến quá nhiều sự trừu tượng. Trong trường hợp đó tôi sẽ sai về phía lặp lại.

Tôi cũng xem xét mức độ kinh nghiệm của các thành viên khác trong nhóm của tôi. Tôi tránh sự trừu tượng "ưa thích" với các nhà phát triển thiếu kinh nghiệm, nhưng tận dụng các mẫu thiết kế được công nhận không bền vững với các nhóm trưởng thành hơn.

Trong sự che giấu, câu trả lời cho câu hỏi của bạn là làm bất cứ điều gì làm cho mã của bạn dễ duy trì nhất. Điều đó có nghĩa là gì trong kịch bản của bạn là do bạn quyết định.


Tôi nghĩ rằng bạn đã đóng đinh một trong những vấn đề của tôi chính xác. Ví dụ tôi đã đưa ra về chức năng mà tôi đã cố gắng không trùng lặp có nhiều khả năng là sự trùng lặp của sự trừu tượng hóa.
lutze

+1. Tôi hiện đang làm việc với một hệ thống lạm dụng sao chép và dán ở mức độ vô lý. Việc tìm ra một phương thức duy nhất từ mã sao chép / dán đã loại bỏ hơn 400 dòng khỏi một trong các lớp của tôi ngày hôm nay. Thật không may, tôi đã tìm thấy nó trong một phương pháp 900 dòng ...
KChaloux

1
Nếu hai cấu trúc mã hiện đang làm điều tương tự, nhưng thay đổi thành một mã thường không có nghĩa là thay đổi đối với mã kia, sao chép / dán có thể tốt hơn chức năng chung được bao thanh toán. Nếu mã sử dụng hai phương thức giống hệt nhau cho từng ký tự làm cùng một mục đích cho các mục đích khác nhau và luôn dựa trên cơ sở lựa chọn phương pháp theo mục đích yêu cầu, thì nếu sau đó mọi thứ cần được thực hiện một chút cho một trong những mục đích đó, chỉ thay đổi phù hợp phương pháp sẽ chỉ ảnh hưởng đến những hành vi nên bị ảnh hưởng. Sử dụng một phương pháp chung duy nhất có thể đã làm cho sự thay đổi khó khăn hơn nhiều .
supercat

7

Nếu các chức năng của bạn trở nên phức tạp hơn và lâu hơn (do đó ít đọc hơn) khi bạn cố gắng làm cho chúng KHÔ, thì bạn đã làm sai. Bạn đang cố gắng đưa quá nhiều chức năng vào một chức năng vì bạn nghĩ rằng đây là cách duy nhất để tránh lặp lại cùng một đoạn mã trong hàm thứ hai.

Tạo mã DRY hầu như luôn có nghĩa là tái cấu trúc chức năng chung cho các chức năng nhỏ hơn. Mỗi chức năng đó nên đơn giản hơn (và do đó dễ đọc hơn) so với chức năng ban đầu. Để giữ mọi thứ trong hàm ban đầu ở cùng mức độ trừu tượng, điều này cũng có thể có nghĩa là cấu trúc lại các phần bổ sung không được sử dụng ở nơi khác. Hàm ban đầu của bạn sau đó trở thành "hàm cấp cao", gọi hàm nhỏ hơn và nó cũng nhỏ hơn và đơn giản hơn.

Do đó, việc này chủ yếu dẫn đến các mức độ trừu tượng khác nhau trong mã của bạn - và một số người tin rằng loại mã như vậy ít đọc hơn. Theo kinh nghiệm của tôi, đây là một ngụy biện. Điểm mấu chốt ở đây là cung cấp cho các hàm nhỏ hơn các tên tốt, giới thiệu các kiểu dữ liệu và trừu tượng được đặt tên tốt có thể dễ dàng nắm bắt bởi người thứ hai. Theo cách đó "DRY" và "khả năng đọc" chỉ nên rất, rất hiếm khi xảy ra xung đột.


2

Mặc dù tôi không chắc chắn chính xác điều gì gây ra vấn đề ở đây, kinh nghiệm của tôi là khi bạn thấy DRY và khả năng đọc xung đột nói rằng đã đến lúc phải cấu trúc lại một cái gì đó.


0

Tôi sẽ chọn có thể đọc (= duy trì) thay vì DRY bất kỳ ngày nào trong tuần. Trong thực tế cả hai thường căn chỉnh, một người có khái niệm về DRY thường sẽ tạo ra (hầu hết) mã có thể đọc được.


2
không có lời giải thích, câu trả lời này có thể trở nên vô dụng trong trường hợp nếu người khác đăng một ý kiến ​​trái ngược. Ví dụ: nếu ai đó đăng một yêu cầu như "Tôi sẽ chọn DRY (= có thể duy trì) thay vì có thể đọc được vào bất kỳ ngày nào trong tuần", câu trả lời này sẽ giúp người đọc chọn ra hai ý kiến ​​trái ngược nhau như thế nào? Xem xét chỉnh sửa ing nó thành một hình dạng tốt hơn
gnat

0

Rất rất rất rất rất rất ít lần trong sự nghiệp của tôi, tôi đã tìm thấy một trường hợp hợp pháp gây khó chịu cho DRY. Làm cho nó dễ đọc trước. Đặt tên cho những điều tốt. Sao chép và dán nếu cần.

Bây giờ lùi lại và nhìn vào tổng số bạn đã tạo ra, giống như một nghệ sĩ lùi lại để xem bức tranh của họ. Bây giờ bạn có thể xác định đúng sự lặp lại.

Mã lặp lại nên được tái cấu trúc thành phương thức riêng của nó hoặc được kéo vào lớp riêng của nó.

Lặp lại mã nên là một phương sách cuối cùng. Đầu tiên có thể đọc và DRY, không thể đọc được nếu bạn giới hạn phạm vi của mã lặp lạ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.