Làm thế nào để bạn biện minh cho nhiều mã được viết bằng cách làm theo các thực hành mã sạch?


106

Người điều hành lưu ý
Câu hỏi này đã có mười bảy câu trả lời được đăng lên nó. Trước khi bạn đăng câu trả lời mới, vui lòng đọc các câu trả lời hiện có và đảm bảo rằng quan điểm của bạn chưa được bảo vệ đầy đủ.

Tôi đã theo dõi một số thực tiễn được đề xuất trong cuốn sách "Mã sạch" của Robert Martin, đặc biệt là những phần mềm áp dụng cho loại phần mềm tôi làm việc và những phần mềm có ý nghĩa đối với tôi (tôi không tuân theo nó như giáo điều) .

Tuy nhiên, một tác dụng phụ tôi nhận thấy là mã "sạch" mà tôi viết, có nhiều mã hơn so với khi tôi không tuân theo một số thực tiễn. Các thực tiễn cụ thể dẫn đến điều này là:

  • Đóng gói có điều kiện

Vì vậy, thay vì

if(contact.email != null && contact.emails.contains('@')

Tôi có thể viết một phương pháp nhỏ như thế này

private Boolean isEmailValid(String email){...}
  • Thay thế một nhận xét nội tuyến bằng một phương thức riêng tư khác, để tên phương thức mô tả chính nó thay vì có một nhận xét nội tuyến trên đầu trang của nó
  • Một lớp chỉ nên có một lý do để thay đổi

Và một vài người khác. Vấn đề là, có thể là một phương thức gồm 30 dòng, kết thúc là một lớp, bởi vì các phương thức nhỏ thay thế các nhận xét và gói gọn các điều kiện, v.v. Khi bạn nhận ra mình có rất nhiều phương thức, thì nó "có ý nghĩa" đặt tất cả các chức năng vào một lớp, khi thực sự nó phải là một phương thức.

Tôi biết rằng bất kỳ thực hành nào được thực hiện đến cùng cực đều có thể gây hại.

Câu hỏi cụ thể tôi đang tìm câu trả lời là:

Đây có phải là một sản phẩm phụ được chấp nhận của việc viết mã sạch? Nếu vậy, một số đối số tôi có thể sử dụng để chứng minh thực tế là có nhiều LỘC đã được viết?

Tổ chức không quan tâm cụ thể về nhiều LỘC hơn, nhưng nhiều LỘC hơn có thể dẫn đến các lớp rất lớn (một lần nữa, có thể được thay thế bằng một phương thức dài mà không có một loạt các hàm trợ giúp sử dụng một lần vì lợi ích dễ đọc).

Khi bạn thấy một lớp đủ lớn, nó sẽ tạo ấn tượng rằng lớp đó đủ bận rộn và trách nhiệm của nó đã được kết thúc. Do đó, bạn có thể, cuối cùng tạo ra nhiều lớp hơn để đạt được các phần chức năng khác. Kết quả là có rất nhiều lớp học, tất cả đều thực hiện "một việc" với sự trợ giúp của nhiều phương thức trợ giúp nhỏ.

ĐÂY là mối quan tâm cụ thể ... những lớp đó có thể là một lớp duy nhất vẫn đạt được "một điều", mà không cần sự trợ giúp của nhiều phương thức nhỏ. Nó có thể là một lớp duy nhất với khoảng 3 hoặc 4 phương thức và một số nhận xét.


98
Nếu tổ chức của bạn chỉ sử dụng LỘC làm số liệu cho các cơ sở mã của bạn, thì việc chứng minh mã sạch sẽ không có gì bắt đầu.
Kilian Foth

24
Nếu khả năng duy trì là mục tiêu của bạn, thì LỘC không phải là thước đo tốt nhất để đánh giá - đó là một trong số đó, nhưng có nhiều điều cần xem xét hơn là chỉ đơn giản là giữ cho nó ngắn gọn.
Zibbobz

29
Không phải là một câu trả lời, nhưng một điểm cần được thực hiện: Có cả một tiểu ban về việc viết mã với càng ít dòng / ký hiệu càng tốt. codegolf.stackexchange.com Người ta có thể lập luận rằng hầu hết các câu trả lời không thể đọc được như họ có thể.
Antitheos

14
Tìm hiểu lý do đằng sau mỗi thực hành tốt nhất không chỉ là quy tắc. Theo quy tắc mà không có lý do là hàng hóa sùng bái. Mỗi quy tắc duy nhất có một lý do riêng của nó.
Gherman

9
Chỉ là một bên, và sử dụng ví dụ của bạn, đôi khi đẩy mọi thứ ra các phương thức sẽ khiến bạn nghĩ: "Có thể có một chức năng thư viện có thể làm điều này". Ví dụ: để xác thực một địa chỉ email, bạn có thể tạo System.Net.Mail.MailAddress để xác thực địa chỉ đó cho bạn. Sau đó, bạn có thể (hy vọng) tin tưởng tác giả của thư viện đó để làm cho đúng. Điều này có nghĩa là cơ sở mã của bạn sẽ có nhiều trừu tượng hơn và giảm kích thước.
Gregory Currie

Câu trả lời:


130

... chúng tôi là một nhóm rất nhỏ hỗ trợ một cơ sở mã tương đối lớn và không có giấy tờ (mà chúng tôi đã kế thừa), vì vậy một số nhà phát triển / quản lý thấy giá trị bằng cách viết ít mã hơn để hoàn thành công việc để chúng tôi có ít mã hơn để duy trì

Những người dân này đã xác định chính xác một cái gì đó: họ muốn mã dễ bảo trì hơn. Dù họ đã sai ở đâu khi cho rằng càng có ít mã thì càng dễ bảo trì.

Để mã dễ bảo trì, thì nó cần phải dễ thay đổi. Cho đến nay, cách dễ nhất để đạt được mã dễ thay đổi là có một bộ đầy đủ các thử nghiệm tự động cho nó sẽ thất bại nếu thay đổi của bạn là một lỗi. Các thử nghiệm là mã, do đó, việc viết các thử nghiệm đó sẽ làm tăng cơ sở mã của bạn. Và đó là một điều tốt.

Thứ hai, để tìm ra những gì cần thay đổi, mã của bạn cần vừa dễ đọc vừa dễ lý luận. Mã rất ngắn gọn, kích thước thu nhỏ chỉ để giữ cho dòng đếm ngược là rất khó để dễ đọc. Rõ ràng có một sự thỏa hiệp sẽ xảy ra vì mã dài hơn sẽ mất nhiều thời gian hơn để đọc. Nhưng nếu nó nhanh hơn để hiểu, thì nó đáng giá. Nếu nó không mang lại lợi ích đó, thì sự dài dòng đó sẽ không còn là lợi ích nữa. Nhưng nếu mã dài hơn cải thiện khả năng đọc thì một lần nữa đây là một điều tốt.


27
"Cho đến nay, cách dễ nhất để đạt được mã dễ thay đổi là có một bộ đầy đủ các thử nghiệm tự động cho nó sẽ thất bại nếu thay đổi của bạn là một lỗi." Đơn giản là nó sai. Các thử nghiệm yêu cầu công việc bổ sung cho mọi thay đổi hành vi bởi vì các thử nghiệm cũng cần thay đổi , đây là do thiết kế và nhiều người cho rằng thay đổi sẽ an toàn hơn, nhưng nó cũng nhất thiết làm thay đổi khó khăn hơn.
Jack Aidley

63
Chắc chắn, nhưng thời gian bị mất trong việc duy trì các xét nghiệm này bị giảm xuống bởi thời gian bạn sẽ mất chẩn đoán và sửa lỗi mà các xét nghiệm ngăn chặn.
MetaFight

29
@JackAidley, việc phải thay đổi các bài kiểm tra cùng với mã có thể mang lại sự xuất hiện của nhiều công việc hơn, nhưng chỉ khi một người bỏ qua các lỗi khó tìm mà thay đổi thành mã chưa được kiểm tra sẽ thường không được tìm thấy cho đến khi giao hàng . Cái sau chỉ đơn thuần là ảo tưởng về công việc ít hơn.
David Arno

31
@JackAidley, tôi hoàn toàn không đồng ý với bạn. Các thử nghiệm làm cho mã dễ dàng thay đổi hơn. Tôi sẽ thừa nhận mặc dù mã được thiết kế tồi đó được ghép quá chặt và do đó kết hợp chặt chẽ với các thử nghiệm có thể khó thay đổi, nhưng mã được kiểm tra tốt, có cấu trúc tốt rất đơn giản để thay đổi theo kinh nghiệm của tôi.
David Arno

22
@JackAidley Bạn có thể cấu trúc lại rất nhiều mà không cần thay đổi bất kỳ API hoặc giao diện nào. Nó có nghĩa là bạn có thể phát điên trong khi sửa đổi mã mà không phải thay đổi một dòng trong các bài kiểm tra đơn vị hoặc chức năng. Đó là, nếu các bài kiểm tra của bạn không kiểm tra một triển khai cụ thể.
Eric Duminil

155

Vâng, đó là một sản phẩm phụ có thể chấp nhận được và lời biện minh là hiện tại nó có cấu trúc sao cho bạn không phải đọc hầu hết các mã trong hầu hết thời gian. Thay vì đọc một hàm 30 dòng mỗi khi bạn thực hiện thay đổi, bạn đang đọc một hàm 5 dòng để có được dòng tổng thể và có thể một vài hàm trợ giúp nếu thay đổi của bạn chạm vào vùng đó. Nếu lớp "thêm" mới của bạn được gọi EmailValidatorvà bạn biết rằng vấn đề của bạn không phải là xác thực email, bạn có thể bỏ qua việc đọc nó hoàn toàn.

Việc sử dụng lại các phần nhỏ hơn cũng dễ dàng hơn, điều này có xu hướng giảm số lượng dòng cho chương trình tổng thể của bạn. An EmailValidatorcó thể được sử dụng ở khắp mọi nơi. Một số dòng mã xác thực email nhưng được kiểm tra cùng với mã truy cập cơ sở dữ liệu không thể được sử dụng lại.

Và sau đó xem xét những gì cần phải được thực hiện nếu các quy tắc xác thực email cần được thay đổi, đó là điều bạn muốn: một địa điểm được biết đến; hoặc nhiều địa điểm, có thể thiếu một vài?


10
Câu trả lời tốt hơn sau đó là thử nghiệm đơn vị Mệt mỏi giải quyết tất cả các vấn đề của bạn Hướng
Dirk Boer

13
Câu trả lời này đánh vào một điểm quan trọng mà chú Bob và bạn bè dường như luôn bỏ lỡ - tái cấu trúc thành các phương thức nhỏ chỉ giúp ích nếu bạn không phải đọc tất cả các phương thức nhỏ để tìm ra mã của bạn đang làm gì. Tạo một lớp riêng để xác nhận địa chỉ email là khôn ngoan. Kéo mã iterations < _maxIterationsvào một phương thức được gọi ShouldContinueToIteratengu ngốc .
BJ Myers

4
@DavidArno: "trở nên hữu ích"! = "Giải quyết tất cả các vấn đề của bạn"
Christian Hackl

2
@DavidArno: Khi một người phàn nàn về những người ngụ ý rằng kiểm thử đơn vị "giải quyết tất cả các vấn đề của bạn", họ rõ ràng có nghĩa là những người ngụ ý rằng kiểm thử đơn vị giải quyết, hoặc ít nhất là đóng góp cho giải pháp, gần như tất cả các vấn đề trong công nghệ phần mềm. Tôi nghĩ không ai buộc tội bất cứ ai đề nghị thử nghiệm đơn vị như một cách để chấm dứt chiến tranh, nghèo đói và bệnh tật. Một cách khác để nói là việc kiểm tra đơn vị cực đoan trong nhiều câu trả lời, không chỉ cho câu hỏi này mà còn về SE nói chung, đang bị chỉ trích (một cách chính xác).
Christian Hackl

2
Xin chào @DavidArno, nhận xét của tôi rõ ràng là một kẻ cường điệu không phải là người rơm;) Đối với tôi nó như thế này: Tôi đang hỏi làm thế nào để chiếc xe của tôi được sửa chữa và những người tôn giáo đến và nói với tôi rằng tôi nên sống một cuộc sống ít tội lỗi hơn. Về lý thuyết, một cái gì đó đáng để thảo luận, nhưng nó không thực sự giúp tôi trở nên tốt hơn trong việc sửa chữa ô tô.
Dirk Boer

34

Bill Gates nổi tiếng được cho là "Đo lường tiến trình lập trình bằng các dòng mã giống như đo tiến độ chế tạo máy bay theo trọng lượng".

Tôi khiêm tốn đồng ý với tình cảm này. Điều này không có nghĩa là một chương trình nên cố gắng nhiều hơn hoặc ít hơn các dòng mã, nhưng cuối cùng đây không phải là thứ được tính để tạo ra một chương trình hoạt động và hoạt động. Nó giúp nhớ rằng cuối cùng lý do đằng sau việc thêm các dòng mã bổ sung là về mặt lý thuyết nó dễ đọc hơn.

Có thể có sự bất đồng về việc một thay đổi cụ thể có thể dễ đọc hơn hay ít hơn, nhưng tôi không nghĩ rằng bạn đã sai khi thực hiện thay đổi cho chương trình của mình bởi vì bạn nghĩ bằng cách đó bạn sẽ làm cho nó dễ đọc hơn. Ví dụ, việc tạo một cái isEmailValidcó thể được coi là thừa và không cần thiết, đặc biệt nếu nó được gọi chính xác một lần bởi lớp định nghĩa nó. Tuy nhiên, tôi muốn nhìn thấy isEmailValidmột điều kiện hơn là một chuỗi các điều kiện ANDed, theo đó tôi phải xác định từng điều kiện riêng lẻ kiểm tra và lý do tại sao nó được kiểm tra.

Trường hợp bạn gặp rắc rối là khi bạn tạo một isEmailValidphương pháp có tác dụng phụ hoặc kiểm tra những thứ khác ngoài e-mail, bởi vì điều này tồi tệ hơn là chỉ đơn giản là viết ra tất cả. Nó tệ hơn vì nó gây hiểu lầm và tôi có thể bỏ lỡ một lỗi vì nó.

Mặc dù rõ ràng bạn không làm điều đó trong trường hợp này, vì vậy tôi sẽ khuyến khích bạn tiếp tục như bạn đang làm. Bạn nên luôn tự hỏi nếu bằng cách thực hiện thay đổi, nó sẽ dễ đọc hơn, và nếu đó là trường hợp của bạn, thì hãy làm điều đó!


1
Trọng lượng máy bay là một số liệu quan trọng, mặc dù. Và trong quá trình thiết kế, trọng lượng dự kiến ​​được theo dõi chặt chẽ. Không phải là một dấu hiệu của sự tiến bộ, mà là một hạn chế. Giám sát các dòng mã cho thấy nhiều hơn là tốt hơn, trong khi trong thiết kế máy bay trọng lượng ít hơn là tốt hơn. Vì vậy, tôi nghĩ rằng ông Gates có thể đã chọn một minh họa tốt hơn cho quan điểm của mình.
jos

21
@jos trong nhóm cụ thể mà OP đang hợp tác, có vẻ như ít LỘC được coi là 'tốt hơn'. Điểm mà Bill Gates đang đưa ra là LỘC không liên quan đến tiến bộ theo bất kỳ cách có ý nghĩa nào, giống như trong trọng lượng chế tạo máy bay không liên quan đến tiến độ theo cách có ý nghĩa. Một chiếc máy bay đang được chế tạo có thể có 95% trọng lượng cuối cùng của nó tương đối nhanh, nhưng nó sẽ chỉ là một cái vỏ rỗng không có hệ thống điều khiển, nó không hoàn thành 95%. Tương tự trong phần mềm, nếu một chương trình có 100k dòng mã, điều đó không có nghĩa là mỗi 1000 dòng cung cấp 1% chức năng.
Mr.Mindor

7
Giám sát tiến độ là một công việc khó khăn, phải không? Quản lý kém.
jos

@jos: trong mã cũng tốt hơn là có ít dòng hơn cho cùng chức năng, nếu tất cả các dòng khác đều bằng nhau.
RemcoGerlich

@jos Đọc kỹ. Gates không nói gì về việc liệu trọng lượng có phải là thước đo quan trọng đối với bản thân máy bay hay không. Ông nói rằng trọng lượng là một thước đo khủng khiếp cho tiến trình chế tạo máy bay. Rốt cuộc, bằng biện pháp đó ngay khi bạn có toàn bộ thân tàu ném xuống đất, về cơ bản bạn đã hoàn thành vì số tiền đó có lẽ chiếm 9x% trọng lượng của toàn bộ máy bay.
Voo

23

vì vậy một số nhà phát triển / quản lý thấy giá trị bằng cách viết ít mã hơn để hoàn thành công việc để chúng tôi có ít mã hơn để duy trì

Đây là một vấn đề mất tầm nhìn về mục tiêu thực tế.

Điều quan trọng là giảm giờ dành cho phát triển . Điều đó được đo bằng thời gian (hoặc nỗ lực tương đương), không phải bằng dòng mã.
Điều này giống như nói rằng các nhà sản xuất ô tô nên chế tạo ô tô của họ với ít ốc vít hơn, bởi vì phải mất một khoảng thời gian khác không để đặt mỗi ốc vít vào. Trong khi đó là chính xác về mặt giáo dục, giá trị thị trường của ô tô không được xác định bởi số lượng ốc vít hoặc không có. Trên hết, một chiếc xe cần phải có hiệu suất, an toàn và dễ bảo trì.

Phần còn lại của câu trả lời là các ví dụ về cách mã sạch có thể dẫn đến tăng thời gian.


Ghi nhật ký

Lấy một ứng dụng (A) không có đăng nhập. Bây giờ tạo ứng dụng B, cùng ứng dụng A nhưng có ghi nhật ký. B sẽ luôn có nhiều dòng mã hơn và do đó bạn cần viết thêm mã.

Nhưng rất nhiều thời gian sẽ chìm vào việc điều tra các vấn đề và lỗi, và tìm ra điều gì đã sai.

Đối với ứng dụng A, các nhà phát triển sẽ bị mắc kẹt khi đọc mã và phải liên tục tái tạo vấn đề và bước qua mã để tìm nguồn gốc của vấn đề. Điều này có nghĩa là nhà phát triển phải kiểm tra từ đầu đến cuối, trong mỗi lớp được sử dụng và cần phải quan sát mọi đoạn logic đã sử dụng.
Có thể anh ấy may mắn tìm thấy nó ngay lập tức, nhưng có lẽ câu trả lời sẽ là ở nơi cuối cùng anh ấy nghĩ đến việc tìm kiếm.

Đối với ứng dụng B, giả sử ghi nhật ký hoàn hảo, nhà phát triển quan sát nhật ký, có thể ngay lập tức xác định thành phần bị lỗi và bây giờ biết nơi để tìm.

Đây có thể là vấn đề của vài phút, giờ hoặc ngày lưu; tùy thuộc vào kích thước và độ phức tạp của codebase.


Hồi quy

Hãy sử dụng ứng dụng A, hoàn toàn không thân thiện với DRY.
Lấy ứng dụng B, là DRY, nhưng cuối cùng lại cần nhiều dòng hơn vì các tóm tắt bổ sung.

Một yêu cầu thay đổi được đệ trình, đòi hỏi phải thay đổi logic.

Đối với ứng dụng B, nhà phát triển thay đổi logic (duy nhất, được chia sẻ) theo yêu cầu thay đổi.

Đối với ứng dụng A, nhà phát triển phải thay đổi tất cả các phiên bản của logic này khi anh ta nhớ nó đang được sử dụng.

  • Nếu anh ta quản lý để nhớ tất cả các trường hợp, anh ta sẽ vẫn phải thực hiện cùng một thay đổi nhiều lần.
  • Nếu anh ta không quản lý để nhớ tất cả các trường hợp, thì bây giờ bạn đang xử lý một cơ sở mã không nhất quán mâu thuẫn với chính nó. Nếu nhà phát triển quên một đoạn mã hiếm khi được sử dụng, lỗi này có thể không rõ ràng đối với người dùng cuối cho đến tương lai. Tại thời điểm đó, người dùng cuối sẽ xác định nguồn gốc của vấn đề là gì? Ngay cả khi như vậy, nhà phát triển có thể không nhớ những gì thay đổi đòi hỏi, và sẽ phải tìm ra cách thay đổi đoạn logic bị lãng quên này. Có thể nhà phát triển thậm chí không làm việc tại công ty trước đó, và sau đó một người khác bây giờ phải tìm ra tất cả từ đầu.

Điều này có thể dẫn đến lãng phí thời gian rất lớn. Không chỉ trong phát triển, mà còn trong việc săn lùng và tìm ra lỗi. Ứng dụng có thể bắt đầu hoạt động thất thường theo cách mà các nhà phát triển không thể dễ dàng hiểu được. Và điều đó sẽ dẫn đến các phiên gỡ lỗi kéo dài.


Khả năng hoán đổi cho nhà phát triển

Nhà phát triển Một ứng dụng đã tạo A. Mã không sạch cũng không thể đọc được, nhưng nó hoạt động như một bùa mê và đang chạy trong sản xuất. Không có gì đáng ngạc nhiên, cũng không có tài liệu.

Nhà phát triển A vắng mặt trong một tháng do ngày lễ. Yêu cầu thay đổi khẩn cấp được nộp. Không thể đợi thêm ba tuần nữa để Dev A trở lại.

Nhà phát triển B phải thực hiện thay đổi này. Bây giờ anh ta cần phải đọc toàn bộ cơ sở mã, hiểu cách mọi thứ hoạt động, tại sao nó hoạt động và những gì nó cố gắng thực hiện. Điều này mất nhiều thời gian, nhưng hãy nói rằng anh ta có thể làm điều đó trong ba tuần.

Đồng thời, ứng dụng B (mà dev B tạo ra) có trường hợp khẩn cấp. Dev B bị chiếm đóng, nhưng Dev C có sẵn, mặc dù anh ta không biết codebase. Chúng ta làm gì?

  • Nếu chúng ta giữ B làm việc trên A và đưa C hoạt động trên B, thì chúng ta có hai nhà phát triển không biết họ đang làm gì và công việc đang được thực hiện dưới mức tối ưu.
  • Nếu chúng ta kéo B ra khỏi A và bắt anh ta làm B, và bây giờ chúng ta đưa C lên A, thì tất cả công việc của nhà phát triển B (hoặc một phần đáng kể của nó) có thể sẽ bị loại bỏ. Điều này có khả năng lãng phí ngày / tuần.

Dev A trở về từ kỳ nghỉ của anh ấy và thấy rằng B không hiểu mã, và do đó đã triển khai nó một cách tồi tệ. Đó không phải là lỗi của B, vì anh ta đã sử dụng tất cả các tài nguyên có sẵn, mã nguồn không thể đọc được đầy đủ. Bây giờ A có phải dành thời gian để sửa lỗi dễ đọc của mã không?


Tất cả những vấn đề này, và nhiều vấn đề khác, kết thúc lãng phí thời gian . Có, trong ngắn hạn, mã sạch đòi hỏi nhiều nỗ lực hơn bây giờ , nhưng cuối cùng nó sẽ trả cổ tức trong tương lai khi các lỗi / thay đổi không thể tránh khỏi cần phải được giải quyết.

Quản lý cần phải hiểu rằng một nhiệm vụ ngắn bây giờ sẽ giúp bạn tiết kiệm một số nhiệm vụ dài trong tương lai. Không lên kế hoạch là kế hoạch thất bại.

Nếu vậy, một số đối số tôi có thể sử dụng để chứng minh thực tế là có nhiều LỘC đã được viết?

Lời giải thích goto của tôi là hỏi quản lý xem họ thích gì hơn: một ứng dụng với cơ sở mã 100KLOC có thể được phát triển trong ba tháng hoặc một cơ sở mã 50KLOC có thể được phát triển trong sáu tháng.

Rõ ràng họ sẽ chọn thời gian phát triển ngắn hơn, bởi vì ban quản lý không quan tâm đến KLOC . Các nhà quản lý tập trung vào KLOC đang quản lý vi mô trong khi không hiểu rõ về những gì họ đang cố gắng quản lý.


23

Tôi nghĩ bạn nên rất cẩn thận về việc áp dụng các thực hành "mã sạch" trong trường hợp chúng dẫn đến sự phức tạp tổng thể hơn. Tái cấu trúc sớm là gốc rễ của nhiều điều xấu.

Trích xuất một điều kiện cho một hàm dẫn đến mã đơn giản hơn tại điểm mà điều kiện được trích xuất từ ​​đó , nhưng dẫn đến sự phức tạp tổng thể hơn bởi vì bây giờ bạn có một hàm có thể nhìn thấy từ nhiều điểm hơn trong chương trình. Bạn thêm một gánh nặng phức tạp nhỏ cho tất cả các chức năng khác nơi chức năng mới này hiện có thể nhìn thấy.

Tôi không nói rằng bạn không nên trích xuất điều kiện, chỉ là bạn nên cân nhắc cẩn thận nếu cần.

  • Nếu bạn muốn kiểm tra cụ thể logic xác thực e-mail. Sau đó, bạn cần trích xuất logic đó sang một hàm riêng biệt - có thể là cả lớp.
  • Nếu cùng một logic được sử dụng từ nhiều nơi trong mã thì rõ ràng bạn phải trích xuất nó thành một hàm duy nhất. Đừng lặp lại chính mình!
  • Nếu logic rõ ràng là một trách nhiệm riêng biệt, ví dụ như xác thực email xảy ra ở giữa thuật toán sắp xếp. Việc xác thực email sẽ thay đổi độc lập với thuật toán sắp xếp, vì vậy chúng phải ở trong các lớp riêng biệt.

Trong tất cả những điều trên là một lý do cho việc trích xuất ngoài việc nó chỉ là "mã sạch". Hơn nữa, bạn có thể thậm chí sẽ không nghi ngờ nếu đó là điều đúng đắn.

Tôi muốn nói, nếu nghi ngờ, luôn chọn mã đơn giản và đơn giản nhất.


7
Tôi phải đồng ý, biến mọi điều kiện thành một phương thức xác nhận có thể đưa ra sự phức tạp không mong muốn hơn khi nói đến bảo trì và đánh giá mã. Bây giờ bạn phải chuyển đổi qua lại trong mã chỉ để đảm bảo các phương thức có điều kiện của bạn là đúng. Và điều gì xảy ra khi bạn có các điều kiện khác nhau cho cùng một giá trị? Bây giờ bạn có thể có một cơn ác mộng đặt tên với một số phương thức nhỏ chỉ được gọi một lần và trông gần giống nhau.
pboss3010

7
Dễ dàng trả lời tốt nhất ở đây. Đặc biệt là sự quan sát (trong đoạn thứ ba) rằng sự phức tạp không chỉ đơn giản là một thuộc tính của toàn bộ mã mà là một cái gì đó tồn tại và khác nhau, trên nhiều mức độ trừu tượng đồng thời.
Christian Hackl

2
Tôi nghĩ rằng một trong những cách để đặt này là, nói chung, giải nén một điều kiện nên được thực hiện chỉ nếu có một ý nghĩa, không obfuscated tên cho tình trạng đó. Đây là một điều kiện cần nhưng không đủ.
JimmyJames

Re "... bởi vì bây giờ bạn có một hàm có thể nhìn thấy từ nhiều điểm hơn trong chương trình" : trong Pascal có thể có các hàm cục bộ - "... Mỗi thủ tục hoặc hàm có thể có các khai báo riêng về nhãn goto, hằng số , loại, biến và các quy trình và chức năng khác, ... "
Peter Mortensen

2
@PeterMortensen: Cũng có thể có trong C # và JavaScript. Và đó là tuyệt vời! Nhưng điểm vẫn còn, một chức năng, thậm chí là một chức năng cục bộ, có thể nhìn thấy trong phạm vi lớn hơn một đoạn mã nội tuyến.
JacquesB

9

Tôi chỉ ra rằng không có gì sai với điều này:

if(contact.email != null && contact.email.contains('@')

Ít nhất là giả sử nó được sử dụng một lần này.

Tôi có thể có vấn đề với điều này rất dễ dàng:

private Boolean isEmailValid(String email){
   return email != null && email.contains('@');
}

Một vài điều tôi muốn xem:

  1. Tại sao nó là riêng tư? Nó trông giống như một cuống có khả năng hữu ích. Nó có đủ hữu ích để trở thành một phương pháp riêng tư và không có cơ hội sử dụng rộng rãi hơn không?
  2. Tôi sẽ không đặt tên cho phương thức IsValidEmail một cách cá nhân, có thể là ContainsAtSign hoặc LooksVaguelyLikeEmailAddress bởi vì nó gần như không có xác nhận thực sự, có thể không tốt, có thể không phải là những gì đã xảy ra.
  3. Có phải nó đang được sử dụng nhiều hơn một lần?

Nếu nó được sử dụng một lần, đơn giản để phân tích cú pháp và mất ít hơn một dòng tôi sẽ đoán lần thứ hai quyết định. Có lẽ đó không phải là điều tôi sẽ gọi nếu đó không phải là vấn đề cụ thể từ một đội.

Mặt khác, tôi đã thấy các phương thức làm một cái gì đó như thế này:

if (contact.email != null && contact.email.contains('@')) { ... }
else if (contact.email != null && contact.email.contains('@') && contact.email.contains("@mydomain.com")) { //headquarters email }
else if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") ) { //internal contract teams }

Ví dụ đó rõ ràng là không KHÔ.

Hoặc thậm chí chỉ là tuyên bố cuối cùng có thể đưa ra một ví dụ khác:

if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") )

Mục tiêu phải là làm cho mã dễ đọc hơn:

if (LooksSortaLikeAnEmail(contact.Email)) { ... }
else if (LooksLikeFromHeadquarters(contact.Email)) { ... }
else if (LooksLikeInternalEmail(contact.Email)) { ... }

Một kịch bản khác:

Bạn có thể có một phương pháp như:

public void SaveContact(Contact contact){
   if (contact.email != null && contact.email.contains('@'))
   {
       contacts.Add(contact);
       contacts.Save();
   }
}

Nếu điều này phù hợp với logic kinh doanh của bạn và không được sử dụng lại thì không có vấn đề gì ở đây.

Nhưng khi ai đó hỏi "Tại sao '@' được lưu, vì điều đó không đúng!" và bạn quyết định thêm xác nhận thực tế của một số loại, sau đó giải nén nó!

Bạn sẽ rất vui vì bạn cũng cần phải có tài khoản email thứ hai của tổng thống Pr3 $ sid3nt @ h0m3! @ Mydomain.com và quyết định chỉ cần ra ngoài và thử và hỗ trợ RFC 2822.

Về khả năng đọc:

// If there is an email property and it contains an @ sign then process
if (contact.email != null && contact.email.contains('@'))

Nếu mã của bạn rõ ràng, bạn không cần bình luận ở đây. Trên thực tế, bạn không cần bình luận để nói mã đang làm gì hầu hết thời gian, mà là tại sao nó lại làm như vậy:

// The UI passes '@' by default, the DBA's made this column non-nullable but 
// marketing is currently more concerned with other fields and '@' default is OK
if (contact.email != null && contact.email.contains('@'))

Cho dù các ý kiến ​​trên một câu lệnh if hoặc bên trong một phương thức nhỏ là đối với tôi, là phạm vi. Tôi thậm chí có thể tranh luận ngược lại về sự hữu ích với những bình luận tốt bên trong một phương pháp khác bởi vì bây giờ bạn sẽ phải điều hướng đến một phương pháp khác để xem cách thức và lý do tại sao nó làm những gì nó làm.

Tóm lại: Đừng đo lường những điều này; Tập trung vào các nguyên tắc mà văn bản được xây dựng từ (DRY, RẮN, KISS).

// A valid class that does nothing
public class Nothing 
{

}

3
Whether the comments above an if statement or inside a tiny method is to me, pedantic.Đây là một vấn đề "rơm làm vỡ lưng lạc đà". Bạn nói đúng rằng điều này không quá khó để đọc thẳng. Nhưng nếu bạn có một phương pháp lớn (ví dụ như một nhập khẩu lớn) trong đó có hàng chục những đánh giá nhỏ, có những gói gọn trong tên phương pháp có thể đọc được ( IsUserActive, GetAverageIncome, MustBeDeleted, ...) sẽ trở thành một sự cải thiện đáng kể về đọc mã. Vấn đề với ví dụ là nó chỉ quan sát một ống hút chứ không phải toàn bộ bó làm vỡ lưng con lạc đà.
Flater

@Flater và tôi hy vọng đây là tinh thần mà người đọc có được từ đó.
AthomSfere

1
"Đóng gói" này là một mô hình chống, và câu trả lời thực sự chứng minh điều này. Chúng tôi quay lại để đọc mã cho mục đích gỡ lỗi và cho mục đích mở rộng mã. Trong cả hai trường hợp, hiểu những gì mã thực sự làm là rất quan trọng. Khối mã bắt đầu if (contact.email != null && contact.email.contains('@'))là lỗi. Nếu if là false, không có gì khác nếu dòng có thể đúng. Điều này hoàn toàn không thể nhìn thấy trong LooksSortaLikeAnEmailkhối. Một hàm chứa một dòng mã không tốt hơn nhiều so với nhận xét giải thích cách hoạt động của dòng.
Quirk

1
Tốt nhất, một lớp khác của cảm ứng che khuất các cơ chế thực tế và làm cho việc gỡ lỗi khó khăn hơn. Tệ nhất, tên hàm đã trở thành một lời nói dối giống như cách các bình luận trở thành lời nói dối - nội dung được cập nhật nhưng tên thì không. Đây không phải là một cuộc đình công chống lại việc đóng gói nói chung, nhưng thành ngữ cụ thể này là triệu chứng của vấn đề hiện đại lớn với kỹ thuật phần mềm "doanh nghiệp" - các lớp và các lớp trừu tượng và keo dính logic logic liên quan.
Quirk

@quirk Tôi nghĩ bạn đồng ý với quan điểm chung của tôi? Và với keo, bạn sẽ đi xuống một vấn đề hoàn toàn khác. Tôi thực sự sử dụng bản đồ mã khi nhìn vào mã nhóm mới. Thật đáng sợ những gì tôi đã thấy đối với một số phương thức lớn gọi một loạt các phương thức lớn ngay cả ở cấp độ mẫu mvc.
AthomSfere

6

Clean Code là một cuốn sách tuyệt vời và rất đáng để đọc, nhưng nó không phải là cơ quan cuối cùng về những vấn đề như vậy.

Việc chia mã thành các hàm logic thường là một ý tưởng tốt, nhưng rất ít lập trình viên làm điều đó đến mức Martin làm - đến một lúc nào đó bạn nhận được lợi nhuận giảm dần từ việc biến mọi thứ thành các hàm và có thể khó theo dõi khi tất cả các mã đều nhỏ miếng.

Một tùy chọn khi không đáng để tạo một hàm hoàn toàn mới là chỉ cần sử dụng một biến trung gian:

boolean isEmailValid = (contact.email != null && contact.emails.contains('@');

if (isEmailValid) {
...

Điều này giúp giữ cho mã dễ theo dõi mà không phải nhảy xung quanh tệp nhiều.

Một vấn đề khác là Clean Code đang trở nên khá cũ như một cuốn sách bây giờ. Rất nhiều công nghệ phần mềm đã chuyển sang hướng lập trình chức năng, trong khi Martin cố gắng thêm trạng thái vào mọi thứ và tạo ra các đối tượng. Tôi nghi ngờ anh ta sẽ viết một cuốn sách hoàn toàn khác nếu anh ta viết nó hôm nay.


Một số lo ngại về dòng mã bổ sung gần điều kiện (tôi hoàn toàn không phải vậy), nhưng có lẽ giải quyết điều đó trong câu trả lời của bạn.
Peter Mortensen

5

Xem xét thực tế rằng điều kiện "là email hợp lệ" mà bạn hiện đang chấp nhận địa chỉ email rất không hợp lệ " @", tôi nghĩ rằng bạn có mọi lý do để trừu tượng hóa một lớp EmailValidator. Thậm chí tốt hơn, sử dụng một thư viện tốt, được kiểm tra tốt để xác nhận địa chỉ email.

Các dòng mã như một số liệu là vô nghĩa. Các câu hỏi quan trọng trong công nghệ phần mềm không phải là:

  • Bạn có quá nhiều mã?
  • Bạn có quá ít mã?

Các câu hỏi quan trọng là:

  • Là toàn bộ ứng dụng được thiết kế chính xác?
  • Là mã thực hiện chính xác?
  • Là mã duy trì?
  • Là mã kiểm tra?
  • Mã đã được kiểm tra đầy đủ chưa?

Tôi chưa bao giờ nghĩ cho LoC khi viết mã cho bất kỳ mục đích nào ngoài Code Golf. Tôi đã tự hỏi mình "Tôi có thể viết điều này ngắn gọn hơn không?", Nhưng với mục đích dễ đọc, duy trì và hiệu quả, không chỉ đơn giản là độ dài.

Chắc chắn, có lẽ tôi có thể sử dụng một chuỗi dài các hoạt động boolean thay vì một phương thức tiện ích, nhưng tôi có nên không?

Câu hỏi của bạn thực sự khiến tôi suy nghĩ lại về một số chuỗi booleans dài mà tôi đã viết và nhận ra rằng có lẽ tôi nên viết một hoặc nhiều phương thức tiện ích thay thế.


3

Ở một cấp độ, họ đúng - ít mã hơn là tốt hơn. Một câu trả lời khác trích dẫn Cổng, tôi thích:

Nếu gỡ lỗi là quá trình loại bỏ các lỗi phần mềm, thì lập trình phải là quá trình đưa chúng vào. Rời - Edsger Dijkstra

Khi gỡ lỗi, người mới chèn mã sửa lỗi; các chuyên gia loại bỏ mã bị lỗi. - - Patt Pattis

Các thành phần rẻ nhất, nhanh nhất và đáng tin cậy nhất là những thành phần không có ở đó. - Gordon Bell

Nói tóm lại, bạn càng có ít mã, càng ít có thể sai. Nếu cái gì đó không cần thiết, thì hãy cắt nó.
Nếu có mã quá phức tạp, thì hãy đơn giản hóa nó cho đến khi các thành phần chức năng thực tế là tất cả những gì còn lại.

Điều quan trọng ở đây là tất cả đều đề cập đến chức năng và chỉ có yêu cầu tối thiểu để thực hiện. Nó không nói bất cứ điều gì về làm thế nào nó được thể hiện.

Những gì bạn đang làm bằng cách cố gắng để có mã sạch không chống lại những điều trên. Bạn đang thêm vào LỘC của mình nhưng không thêm chức năng không sử dụng.

Mục tiêu cuối cùng là có mã dễ đọc nhưng không có phần bổ sung thừa. Hai nguyên tắc không nên hành động chống lại nhau.

Một phép ẩn dụ sẽ được xây dựng một chiếc xe hơi. Phần chức năng của mã là khung xe, động cơ, bánh xe ... những gì làm cho chiếc xe chạy. Làm thế nào bạn phá vỡ nó giống như hệ thống treo, tay lái trợ lực và như vậy, nó làm cho nó dễ dàng hơn để xử lý. Bạn muốn cơ khí của mình đơn giản nhất có thể trong khi vẫn thực hiện công việc của mình, để giảm thiểu khả năng xảy ra sự cố, nhưng điều đó không ngăn bạn có chỗ ngồi đẹp.


2

Có rất nhiều sự khôn ngoan trong các câu trả lời hiện có, nhưng tôi muốn thêm vào một yếu tố nữa: ngôn ngữ .

Một số ngôn ngữ mất nhiều mã hơn những ngôn ngữ khác để có được hiệu ứng tương tự. Đặc biệt, trong khi Java (mà tôi nghi ngờ là ngôn ngữ trong câu hỏi) cực kỳ nổi tiếng và nói chung là rất chắc chắn và rõ ràng và đơn giản, một số ngôn ngữ hiện đại hơn ngắn gọn và biểu cảm hơn nhiều.

Ví dụ, trong Java, có thể dễ dàng lấy 50 dòng để viết một lớp mới với ba thuộc tính, mỗi thuộc tính có một getter và setter và một hoặc nhiều hàm tạo - trong khi bạn có thể thực hiện chính xác như vậy trong một dòng duy nhất của Kotlin * hoặc Scala. (Tiết kiệm Thậm chí nhiều hơn nếu bạn cũng muốn phù hợp equals(), hashCode()toString()phương pháp.)

Kết quả là trong Java, công việc bổ sung có nghĩa là bạn có nhiều khả năng sử dụng lại một đối tượng chung không thực sự phù hợp, để ép các thuộc tính vào các đối tượng hiện có hoặc chuyển một loạt các thuộc tính 'trần' xung quanh; trong khi bằng một ngôn ngữ ngắn gọn, biểu cảm, bạn có nhiều khả năng viết mã tốt hơn.

(Điều này nhấn mạnh sự khác biệt giữa độ phức tạp của 'bề mặt' của mã và độ phức tạp của các ý tưởng / mô hình / xử lý mà nó thực hiện. Các dòng mã không phải là thước đo xấu của lần đầu tiên, nhưng ít liên quan đến lần thứ hai .)

Vì vậy, 'chi phí' của việc làm đúng phụ thuộc vào ngôn ngữ. Có lẽ một dấu hiệu của một ngôn ngữ tốt là một ngôn ngữ không khiến bạn lựa chọn giữa làm tốt mọi thứ và thực hiện chúng một cách đơn giản!

(* Đây thực sự không phải là nơi để cắm, nhưng Kotlin rất đáng để xem IMHO.)


1

Hãy giả sử rằng bạn đang làm việc với lớp Contacthiện tại. Việc bạn đang viết một phương thức khác để xác nhận địa chỉ email là bằng chứng cho thấy rằng lớp Contactkhông xử lý một trách nhiệm duy nhất.

Nó cũng xử lý một số trách nhiệm email, lý tưởng nhất, nên là lớp riêng của nó.


Bằng chứng nữa là mã của bạn là hợp nhất ContactEmaillớp là bạn sẽ không thể kiểm tra mã xác thực email một cách dễ dàng. Nó sẽ đòi hỏi rất nhiều thao tác để đạt được mã xác thực email trong một phương thức lớn với các giá trị phù hợp. Xem phương pháp viz dưới đây.

private void LargeMethod() {
    //A lot of code which modifies a lot of values. You do all sorts of tricks here.
    //Code.
    //Code..
    //Code...

    //Email validation code becoming very difficult to test as it will be difficult to ensure 
    //that you have the right data till you reach here in the method
    ValidateEmail();

    //Another whole lot of code that modifies all sorts of values.
    //Extra work to preserve the result of ValidateEmail() for your asserts later.
}

Mặt khác, nếu bạn có một lớp Email riêng với phương thức xác thực email, thì để kiểm tra đơn vị mã xác thực của bạn, bạn sẽ chỉ thực hiện một cuộc gọi đơn giản Email.Validation()với dữ liệu kiểm tra của mình.


Nội dung phần thưởng: Cuộc nói chuyện của MFeather về sự phối hợp sâu sắc giữa khả năng kiểm tra và thiết kế tốt.


1

Giảm trong LỘC đã được tìm thấy có tương quan với giảm khuyết tật, không có gì khác. Giả sử sau đó bất cứ khi nào bạn giảm LỘC, bạn đã giảm cơ hội khiếm khuyết về cơ bản rơi vào cái bẫy tin rằng mối tương quan tương đương với quan hệ nhân quả. LỘC giảm là kết quả của các thực tiễn phát triển tốt và không phải là điều làm cho mã tốt.

Theo kinh nghiệm của tôi, những người có thể giải quyết vấn đề với ít mã hơn (ở cấp độ vĩ mô) có xu hướng thành thạo hơn những người viết nhiều mã hơn để làm điều tương tự. Những gì các nhà phát triển lành nghề này làm để giảm các dòng mã là sử dụng / tạo trừu tượng và các giải pháp tái sử dụng để giải quyết các vấn đề phổ biến. Họ không dành thời gian đếm các dòng mã và đau đớn về việc họ có thể cắt một dòng ở đây hay ở đó. Thông thường mã họ viết là dài dòng hơn mức cần thiết, họ chỉ viết ít hơn.

Tôi sẽ cho bạn một ví dụ. Tôi đã phải đối phó với logic trong khoảng thời gian và cách chúng trùng nhau, liệu chúng có liền kề nhau không, và khoảng cách nào tồn tại giữa chúng. Khi tôi mới bắt đầu làm việc với những vấn đề này, tôi sẽ có các khối mã thực hiện các phép tính ở mọi nơi. Cuối cùng, tôi đã xây dựng các lớp để biểu diễn các khoảng thời gian và các thao tác tính toán chồng chéo, bổ sung, v.v ... Điều này ngay lập tức loại bỏ các luồng mã lớn và biến chúng thành một vài lệnh gọi phương thức. Nhưng bản thân những lớp học đó không được viết chặt chẽ chút nào.

Nói rõ ràng: nếu bạn đang cố gắng giảm LỘC bằng cách cố gắng cắt một dòng mã ở đây hoặc ở đó với nhiều thông tin hơn, bạn đã làm sai. Nó giống như cố gắng giảm cân bằng cách giảm lượng rau bạn ăn. Viết mã dễ hiểu, duy trì và gỡ lỗi và giảm LỘC thông qua việc sử dụng lại và trừu tượng hóa.


1

Bạn đã xác định một sự đánh đổi hợp lệ

Vì vậy, thực sự có một sự đánh đổi ở đây và nó vốn dĩsự trừu tượng nói chung. Bất cứ khi nào bất cứ ai cố gắng kéo N dòng mã vào chức năng riêng của mình để đặt tên và cách ly nó, họ đồng thời làm cho việc đọc trang web gọi dễ dàng hơn (bằng cách tham khảo một tên thay vì tất cả các chi tiết cơ bản có tên đó) và phức tạp hơn (bây giờ bạn có nghĩa là vướng vào hai phần khác nhau của cơ sở mã). "Dễ" là đối lập với "khó", nhưng nó không phải là từ đồng nghĩa với "đơn giản" mà trái ngược với "phức tạp". Cả hai không đối lập nhau, và sự trừu tượng luôn làm tăng sự phức tạp để chèn một dạng nào đó hoặc dạng khác một cách dễ dàng.

Chúng ta có thể thấy sự phức tạp được thêm trực tiếp khi một số thay đổi trong yêu cầu kinh doanh làm cho sự trừu tượng bắt đầu bị rò rỉ. Có thể một số logic mới sẽ diễn ra một cách tự nhiên nhất ở giữa mã được trừu tượng hóa, ví dụ, nếu mã được trừu tượng đi ngang qua một số cây và bạn thực sự muốn thu thập (và có thể hành động) một số loại thông tin trong khi bạn đi ngang qua cây. Trong khi đó, nếu bạn đã trừu tượng hóa mã này, thì có thể có các trang web cuộc gọi khác và việc thêm logic cần thiết vào giữa phương thức có thể phá vỡ các trang web cuộc gọi khác đó. Hãy xem, bất cứ khi nào chúng ta thay đổi một dòng mã, chúng ta chỉ cần nhìn vào bối cảnh ngay lập tức của dòng mã đó; khi chúng tôi thay đổi một phương thức, chúng tôi phải Cmd-F toàn bộ mã nguồn của chúng tôi để tìm kiếm bất cứ thứ gì có thể bị phá vỡ do thay đổi hợp đồng của phương thức đó,

Thuật toán tham lam có thể thất bại trong những trường hợp này

Sự phức tạp cũng đã làm cho mã theo một nghĩa nào đó khó đọc hơn là nhiều hơn. Trong một công việc trước đây, tôi đã xử lý API HTTP được cấu trúc rất cẩn thận và chính xác thành nhiều lớp, mọi điểm cuối được chỉ định bởi bộ điều khiển xác nhận hình dạng của thông báo đến và sau đó đưa nó cho trình quản lý "lớp logic nghiệp vụ" , sau đó đã đưa ra một số yêu cầu của một số "lớp dữ liệu" chịu trách nhiệm thực hiện một số truy vấn cho một số lớp "đối tượng truy cập dữ liệu", chịu trách nhiệm tạo ra một số Đại biểu SQL thực sự sẽ trả lời câu hỏi của bạn. Điều đầu tiên tôi có thể nói về nó là, một cái gì đó giống như 90% mã là bản sao chép và dán, nói cách khác, nó không phải là op-op. Vì vậy, trong nhiều trường hợp, việc đọc bất kỳ đoạn mã đã cho nào là rất "dễ dàng", bởi vì "oh người quản lý này chỉ chuyển tiếp yêu cầu đến đối tượng truy cập dữ liệu đó".Rất nhiều chuyển đổi ngữ cảnh và tìm tệp và cố gắng theo dõi thông tin mà bạn không bao giờ nên theo dõi ", cái này được gọi là X ở lớp này, nó được gọi là X 'ở lớp khác, sau đó nó được gọi là X' 'trong lớp khác lớp khác. "

Tôi nghĩ rằng khi tôi rời đi, API CRUD đơn giản này đã ở giai đoạn mà nếu bạn in nó ở 30 dòng trên mỗi trang, nó sẽ chiếm tới 10-20 cuốn sách giáo khoa năm trăm trang trên kệ: đó là toàn bộ bách khoa toàn thư lặp đi lặp lại mã. Về độ phức tạp thiết yếu, tôi không chắc rằng thậm chí có một nửa cuốn sách giáo khoa về độ phức tạp thiết yếu trong đó; chúng tôi chỉ có thể có 5-6 sơ đồ cơ sở dữ liệu để xử lý nó. Thực hiện bất kỳ thay đổi nhỏ nào đối với nó là một con voi ma mút, học nó là một con voi ma mút, thêm chức năng mới đã gây đau đớn đến mức chúng ta thực sự có các tệp mẫu nồi hơi mà chúng ta sẽ sử dụng để thêm chức năng mới.

Vì vậy, tôi đã thấy tận mắt làm thế nào để làm cho mỗi phần rất dễ đọc và rõ ràng có thể làm cho toàn bộ rất khó đọc và không rõ ràng. Điều này có nghĩa là thuật toán tham lam có thể thất bại. Bạn biết thuật toán tham lam, đúng chứ? "Tôi sẽ làm bất cứ bước nào cục bộ cải thiện tình hình nhiều nhất, và sau đó tôi sẽ tin tưởng rằng tôi thấy mình trong một tình huống được cải thiện toàn cầu." Nó thường là một nỗ lực đầu tiên đẹp nhưng nó cũng có thể bỏ lỡ trong bối cảnh phức tạp. Ví dụ, trong sản xuất, bạn có thể cố gắng tăng hiệu quả của từng bước cụ thể trong quy trình sản xuất phức tạp - thực hiện các lô lớn hơn, la mắng những người trên sàn dường như không làm gì để khiến họ bận rộn với việc khác - và điều này thường có thể phá hủy hiệu quả toàn cầu của hệ thống.

Thực hành tốt nhất: sử dụng DRY và độ dài để thực hiện cuộc gọi

(Lưu ý: Tiêu đề của phần này là một trò đùa; Tôi thường nói với bạn bè của mình rằng khi ai đó nói rằng "chúng ta nên làm X vì thực tiễn tốt nhất nói như vậy ", họ dành 90% thời gian để không nói về điều gì đó như SQL băm hoặc băm mật khẩu hoặc bất cứ điều gì - thực tiễn tốt nhất đơn phương - và vì vậy, tuyên bố có thể được dịch trong 90% thời gian đó thành "chúng ta nên làm X vì tôi nói như vậy ." Giống như họ có thể có một số bài viết trên blog từ một số doanh nghiệp đã làm việc tốt hơn với X chứ không phải X 'nhưng nhìn chung không có gì đảm bảo rằng doanh nghiệp của bạn giống với doanh nghiệp đó và nói chung có một số bài viết khác từ một số doanh nghiệp khác đã làm việc tốt hơn với X' thay vì X. Vì vậy, vui lòng không lấy tiêu đề nghiêm túc.)

Những gì tôi muốn giới thiệu dựa trên bài nói chuyện của Jack Diederich có tên là Stop Writing Classes (youtube.com) . Anh ta đưa ra một số điểm tuyệt vời trong cuộc nói chuyện đó: ví dụ, bạn có thể biết một lớp thực sự chỉ là một hàm khi nó chỉ có hai phương thức chung và một trong số đó là hàm tạo / bộ khởi tạo. Nhưng trong một trường hợp, anh ta đang nói về việc một thư viện giả định mà anh ta đã thay thế chuỗi cho cuộc nói chuyện như thế nào khi "Muffin" tuyên bố lớp "MuffinHash" của riêng mình, đó là một lớp con của dictloại dựng sẵn mà Python có. Việc triển khai hoàn toàn trống rỗng mà một người nào đó đã nghĩ, "chúng ta có thể cần thêm chức năng tùy chỉnh vào từ điển Python sau, hãy giới thiệu một sự trừu tượng ngay bây giờ chỉ trong trường hợp."

Và phản ứng thách thức của anh ta chỉ đơn giản là, "chúng ta luôn có thể làm điều đó sau, nếu chúng ta cần."

Tôi nghĩ rằng đôi khi chúng ta giả vờ như chúng ta sẽ trở thành những lập trình viên tồi tệ hơn trong tương lai so với hiện tại, vì vậy chúng ta có thể muốn chèn một vài thứ nhỏ nhặt có thể khiến chúng ta hạnh phúc trong tương lai. Chúng tôi dự đoán nhu cầu của chúng tôi trong tương lai. "Nếu lưu lượng truy cập lớn hơn 100 lần so với chúng tôi nghĩ, cách tiếp cận đó sẽ không có quy mô, vì vậy chúng tôi phải đưa khoản đầu tư trực tiếp vào phương pháp khó hơn này sẽ mở rộng quy mô." Rất đáng ngờ.

Nếu chúng ta thực hiện lời khuyên đó một cách nghiêm túc thì chúng ta cần xác định khi nào thì sau này đã đến. Có lẽ điều rõ ràng nhất sẽ là thiết lập một giới hạn trên về độ dài của mọi thứ vì lý do phong cách. Và tôi nghĩ lời khuyên tốt nhất còn lại sẽ là sử dụng DRY, đừng lặp lại chính mình với các heuristic về độ dài đường thẳng để vá lỗ hổng trong các nguyên tắc RẮN. Dựa trên heuristic của 30 dòng là một "trang" văn bản và tương tự với văn xuôi,

  1. Tái cấu trúc một kiểm tra thành một chức năng / phương thức khi bạn muốn sao chép-dán nó. Giống như thỉnh thoảng có những lý do hợp lệ để sao chép-dán nhưng bạn sẽ luôn cảm thấy bẩn về nó. Các tác giả thực sự không khiến bạn đọc lại một câu dài lớn 50 lần trong suốt câu chuyện trừ khi họ thực sự cố gắng làm nổi bật một chủ đề.
  2. Một hàm / phương thức lý tưởng là một "đoạn". Hầu hết các chức năng nên dài khoảng một nửa trang hoặc 1-15 dòng mã và chỉ có thể cho phép 10% các chức năng của bạn nằm trong phạm vi một trang và một nửa, 45 dòng trở lên. Khi bạn đang ở hơn 120 dòng mã và nhận xét rằng điều đó cần được chia thành nhiều phần.
  3. Một tập tin lý tưởng phải là một "chương". Hầu hết các tệp phải dài 12 trang trở xuống, do đó, 360 dòng mã và nhận xét. Chỉ có thể cho phép 10% tệp của bạn dài đến 50 trang hoặc 1500 dòng mã và nhận xét.
  4. Lý tưởng nhất, hầu hết mã của bạn nên được thụt vào với đường cơ sở của hàm hoặc sâu một cấp. Dựa trên một số phương pháp phỏng đoán về cây nguồn Linux, nếu bạn tôn giáo về nó, chỉ có thể 10% mã của bạn được thụt lề 2 cấp trở lên trong đường cơ sở, dưới 5% được thụt lề 3 cấp trở lên. Điều này có nghĩa đặc biệt là những thứ cần "bọc" một số mối quan tâm khác, như xử lý lỗi trong một lần thử / bắt lớn, nên được rút ra khỏi logic thực tế.

Giống như tôi đã đề cập ở đó, tôi đã kiểm tra các số liệu thống kê này dựa trên cây nguồn Linux hiện tại để tìm ra các tỷ lệ phần trăm gần đúng đó, nhưng chúng cũng sắp xếp theo lý do tương tự văn học.

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.