Có phải phương pháp dài luôn xấu? [đóng cửa]


64

Vì vậy, nhìn xung quanh sớm hơn tôi nhận thấy một số ý kiến ​​về các phương pháp dài là thực hành xấu.

Tôi không chắc chắn rằng tôi luôn đồng ý rằng các phương pháp dài là xấu (và muốn ý kiến ​​từ người khác).

Ví dụ, tôi có một số khung nhìn Django xử lý một chút các đối tượng trước khi gửi chúng tới khung nhìn, một phương thức dài là 350 dòng mã. Tôi có mã được viết để nó xử lý các tham số - sắp xếp / lọc bộ truy vấn, sau đó từng chút một xử lý trên các đối tượng mà truy vấn của tôi đã trả về.

Vì vậy, việc xử lý chủ yếu là tập hợp có điều kiện, có các quy tắc đủ phức tạp, nó không thể dễ dàng thực hiện trong cơ sở dữ liệu, vì vậy tôi có một số biến được khai báo bên ngoài vòng lặp chính sau đó bị thay đổi trong vòng lặp.

variable_1 = 0
variable_2 = 0
for object in queryset :
     if object.condition_condition_a and variable_2 > 0 :
     variable 1+= 1
     .....
     ...    
     . 
      more conditions to alter the variables

return queryset, and context 

Vì vậy, theo lý thuyết tôi nên đưa ra tất cả các mã thành các phương thức nhỏ hơn, để tôi có phương thức xem là tối đa một trang dài.

Tuy nhiên, trước đây tôi đã làm việc trên nhiều cơ sở mã khác nhau, đôi khi tôi thấy nó làm cho mã ít đọc hơn, khi bạn cần liên tục chuyển từ phương thức này sang phương pháp tiếp theo để tìm ra tất cả các phần của nó, trong khi vẫn giữ phương thức ngoài cùng trong đầu bạn.

Tôi thấy rằng có một phương thức dài được định dạng tốt, bạn có thể thấy logic dễ dàng hơn, vì nó không bị ẩn đi trong các phương thức bên trong.

Tôi có thể đưa mã vào các phương thức nhỏ hơn, nhưng thường có một vòng lặp bên trong được sử dụng cho hai hoặc ba điều, vì vậy nó sẽ dẫn đến mã phức tạp hơn hoặc các phương thức không làm một việc nào ngoài hai hoặc ba (thay thế Tôi có thể lặp lại các vòng lặp bên trong cho mỗi nhiệm vụ, nhưng sau đó sẽ có một hiệu suất thành công).

Vì vậy, có một trường hợp phương pháp dài không phải lúc nào cũng xấu? Luôn luôn có một trường hợp cho các phương pháp viết, khi chúng sẽ chỉ được sử dụng ở một nơi?

CẬP NHẬT: Có vẻ như tôi đã hỏi câu hỏi này hơn một năm trước.

Vì vậy, tôi đã cấu trúc lại mã sau phản hồi (hỗn hợp) ở đây, chia nó thành các phương thức. Đây là một ứng dụng Django lấy ra các bộ đối tượng phức tạp từ cơ sở dữ liệu, do đó, đối số thử nghiệm đã bị loại bỏ (có lẽ phải mất hầu hết thời gian trong năm để tạo các đối tượng có liên quan cho các trường hợp thử nghiệm. môi trường làm việc trước khi ai đó phàn nàn). Sửa lỗi trong phần mã đó bây giờ dễ dàng hơn một chút, nhưng không ồ ạt như vậy.

trước :

#comment 1 
bit of (uncomplicated) code 1a  
bit of code 2a

#comment 2 
bit of code 2a
bit of code 2b
bit of code 2c

#comment 3
bit of code 3

hiện nay:

method_call_1
method_call_2
method_call_3

def method_1 
    bit of (uncomplicated) code 1a  
    bit of code 2a

def method_2 
    bit of code 2a
    bit of code 2b
    bit of code 2c

def method_3
    bit of code 3

156
Tất cả tuyệt đối là xấu. Luôn luôn.
Joachim Sauer

30
Tôi thấy đối số "trích xuất phương thức nếu bạn có thể sử dụng lại chúng" khá thường xuyên (ở dạng này hoặc dạng tương tự), nhưng tôi không mua nó: nếu phương thức đó đang làm nhiều hơn một điều, bạn nên trích xuất phương thức từ đó để dễ đọc / khả năng bảo trì ngay cả khi những phương thức mới đó được gọi từ chỉ một điểm trong mã của bạn.
Joachim Sauer

4
@gnat: wow, không phải một loại nội tuyến thủ công (thông qua bộ tiền xử lý) trong bước xây dựng sẽ là một giải pháp tốt hơn ở đây?
Joachim Sauer

11
Một trong những lập trình viên giỏi nhất mà tôi biết đã nhận xét rằng nếu bạn thực sự muốn đo, số lượng biến cục bộ là tốt hơn so với độ dài thực tế. Ông đang làm việc trên một trình tối ưu hóa đường dẫn phức tạp trong đó ruột là một phương thức dài hàng trăm dòng, nhưng lượng trạng thái (biến cục bộ) được giữ là rất nhỏ.
Chuu

2
Phương pháp dài không phải lúc nào cũng xấu; nhưng chúng là thứ mà bạn nên luôn luôn nhìn vào và tự hỏi "điều này có tệ không?"
Carson63000

Câu trả lời:


80

Không, phương pháp dài không phải lúc nào cũng xấu.

Trong cuốn sách Hoàn thành mã , người ta đo được rằng các phương pháp dài đôi khi nhanh hơn và dễ viết hơn và không dẫn đến các vấn đề về bảo trì.

Trong thực tế, điều thực sự quan trọng là giữ DRY và tôn trọng sự tách biệt các mối quan tâm. Đôi khi, tính toán chỉ dài để viết, nhưng thực sự sẽ không gây ra vấn đề gì trong tương lai.

Tuy nhiên, từ kinh nghiệm cá nhân của tôi, hầu hết các phương pháp dài có xu hướng thiếu sự quan tâm. Trong thực tế, các phương thức dài là một cách dễ dàng để phát hiện ra rằng có thể có gì đó sai trong mã và cần có sự chăm sóc đặc biệt ở đây khi thực hiện đánh giá mã.

EDIT: Khi bình luận được đưa ra, tôi thêm một điểm thú vị cho câu trả lời. Trên thực tế tôi cũng sẽ kiểm tra các số liệu độ phức tạp cho chức năng (NPATH, độ phức tạp chu kỳ hoặc thậm chí CRAP tốt hơn).

Trên thực tế, tôi khuyên bạn không nên kiểm tra các số liệu như vậy trên các chức năng dài, nhưng nên bao gồm cảnh báo về chúng bằng các công cụ tự động (chẳng hạn như kiểu kiểm tra cho java chẳng hạn) TRÊN MỌI CHỨC NĂNG.


41
+1: "Không, các phương thức dài không phải là xấu" nhưng chúng hầu như luôn luôn xấu
Nhị phân nhị phân

67
Các thân phương thức dài là một mùi mã cổ điển : bản thân nó không phải là vấn đề, nhưng đó là một dấu hiệu cho thấy có lẽ có vấn đề ở đó.
Joachim Sauer

6
+1, nhưng tôi vẫn khuyên bạn nên kiểm tra độ phức tạp theo chu kỳ của phương pháp dài. Các giá trị cao biểu thị các phương thức không thể kiểm tra đơn vị một cách hiệu quả (và các phương thức dài rất hiếm khi không có logic điều khiển luồng).
Daniel B

11
Tôi sử dụng tên phương thức để giảm thiểu ý kiến. Điều này đôi khi dẫn đến những thứ như "getSelectedNodeWithChildren", nhưng đồng nghiệp của tôi cứ nói với tôi rằng mã của tôi rất dễ đọc. Tôi cũng cố gắng tránh viết tắt, chúng rất hay để viết, nhưng không hay để đọc.
K ..

4
@ da_b0uncer Đó cũng là một chính sách tôi tuân theo. Đọc mã khó hơn viết, vì vậy, nỗ lực thêm khi viết để làm cho mã dễ đọc hơn sẽ trả lại.
deadalnix

55

Hầu hết sự tập trung ở đây dường như luôn xoay quanh từ này . Đúng, tuyệt đối là xấu, và công nghệ phần mềm gần như là nghệ thuật, và tất cả những thứ đó ... nhưng tôi sẽ phải nói rằng với ví dụ bạn đã đưa ra, phương pháp sẽ tốt hơn nếu nó được chia lên. Đây là những đối số tôi thường sử dụng để biện minh cho việc chia nhỏ phương thức của bạn:

Khả năng đọc: Tôi không chắc chắn về người khác, nhưng tôi không thể đọc nhanh 350 dòng mã. Vâng, nếu đó là mã của riêng tôi và tôi có thể đưa ra nhiều giả định về nó, tôi có thể lướt qua nó rất nhanh, nhưng đó là điểm chính. Xem xét phương pháp đó sẽ dễ đọc hơn bao nhiêu, nếu nó bao gồm 10 cuộc gọi đến các phương thức khác (mỗi phương thức có một tên mô tả). Làm điều này, bạn đã giới thiệu một lớp trong mã và phương thức cấp cao đưa ra một phác thảo ngắn, ngọt ngào cho người đọc về những gì đang diễn ra.

Chỉnh sửa - để đặt nó ở một khía cạnh khác, hãy nghĩ về nó giống như thế này: bạn sẽ giải thích phương pháp đó như thế nào với một thành viên mới trong nhóm? Chắc chắn nó có một số cấu trúc mà bạn có thể tóm tắt dọc theo dòng "tốt, nó bắt đầu làm A, rồi B, đôi khi C, v.v.". Có một phương pháp "tổng quan" ngắn gọi các phương thức khác làm cho cấu trúc này rõ ràng. Rất hiếm khi tìm thấy 350 dòng mã không mang lại lợi ích; bộ não con người không có nghĩa là phải xử lý danh sách dài 100 mục, chúng tôi nhóm chúng lại.

Khả năng sử dụng lại: Các phương pháp dài có xu hướng có độ gắn kết thấp - chúng thường làm nhiều hơn một việc. Sự gắn kết thấp là kẻ thù của tái sử dụng; nếu bạn kết hợp nhiều tác vụ vào một phương thức, cuối cùng nó sẽ được sử dụng lại ở ít nơi hơn so với lẽ ra.

Khả năng kiểm tra và sự gắn kết: Tôi đã đề cập đến độ phức tạp theo chu kỳ trong một nhận xét ở trên - đó là một thước đo khá tốt về mức độ phức tạp của phương pháp của bạn. Nó đại diện cho giới hạn dưới của số lượng đường dẫn duy nhất thông qua mã của bạn, tùy thuộc vào các đầu vào (chỉnh sửa: được sửa theo nhận xét của MichaelT). Điều đó cũng có nghĩa là để kiểm tra đúng phương pháp của bạn, bạn cần phải có ít nhất nhiều trường hợp kiểm tra bằng số độ phức tạp chu kỳ của mình. Thật không may, khi bạn kết hợp các đoạn mã không thực sự phụ thuộc lẫn nhau, không có cách nào để chắc chắn về sự thiếu phụ thuộc này và sự phức tạp có xu hướng nhân lên với nhau. Bạn có thể nghĩ về biện pháp này như một chỉ dẫn về số lượng những điều khác nhau mà bạn đang cố gắng làm. Nếu nó quá cao, đã đến lúc phân chia và chinh phục.

Tái cấu trúc và cấu trúc: Các phương thức dài thường là dấu hiệu cho thấy một số cấu trúc đang thiếu mã. Thông thường, nhà phát triển không thể tìm ra điểm tương đồng giữa các phần khác nhau của phương thức đó và nơi một đường có thể được vẽ giữa chúng. Nhận ra rằng một phương thức dài là một vấn đề và cố gắng chia nó thành các phương thức nhỏ hơn là bước đầu tiên trên con đường dài hơn để thực sự xác định một cấu trúc tốt hơn cho toàn bộ. Có lẽ bạn cần tạo một hoặc hai lớp; cuối cùng không nhất thiết phải phức tạp hơn!

Tôi cũng nghĩ rằng trong trường hợp này, lý do để có một phương thức dài là "... một số biến được khai báo bên ngoài vòng lặp chính sau đó bị thay đổi trong vòng lặp". Tôi không phải là chuyên gia về Python, nhưng tôi khá chắc chắn rằng vấn đề này có thể được khắc phục một cách tầm thường bằng một số hình thức chuyển qua tham chiếu.


13
+1 để từ chối phần luôn của câu hỏi và tập trung vào thịt: Phương pháp dài hay không là xấu. Tôi nghĩ rằng OP đang tìm kiếm sự biện minh như thể kịch bản của anh ta là một trường hợp vượt trội, mặc dù thông thường khi tôi nghe mọi người giải thích các thực hành xấu của họ là cần thiết cho kịch bản không phổ biến, thì đó là vì họ đã không cố gắng sử dụng các thực tiễn tốt. Các kịch bản không phổ biến thực sự rất không phổ biến, các phương pháp dài thật đáng buồn tuy nhiên khá phổ biến.
Jimmy Hoffa

2
OK nhìn vào danh sách trên: khả năng đọc tôi có thể nói từ kinh nghiệm trong quá khứ được tăng lên nhờ có phương thức lâu hơn, chứa nhiều bình luận và được định dạng tốt, thay vì phải chuyển mã từ phương thức này sang phương thức khác Mặc dù điều này có thể khá chủ quan. Tôi không mong đợi các phần của mã sẽ được sử dụng lại. Hầu hết việc sử dụng lại mã được thực hiện từ sự kế thừa tại thời điểm này.
wobbily_col

1
@wobbily_col BTW, nếu bạn đang tìm kiếm một văn bản được viết tốt đi qua lý do có các phương pháp ngắn hơn, hãy đọc một vài chương đầu tiên của Clean Code , đây là một cách giải thích khá tốt.
Daniel B

5
@wobbily_col Bạn nói rằng phải nhảy quá nhiều để hiểu mã với nhiều phương thức là khó hiểu, tôi nghĩ điểm thiếu ở đây là về tầm quan trọng của việc đặt tên. Nếu một phương thức được đặt tên tốt, bạn không cần nhìn vào nó để tìm hiểu xem nó đang làm gì, thì phương thức gọi đó hoàn toàn dễ hiểu mà không có bất kỳ kiến ​​thức cơ bản nào về những phương thức mà nó gọi đang làm, ví dụ như bạn đã từng sử dụng someVar.toString()và cảm nhận bạn cần xem mã của toString để biết nó đang làm gì? Bạn chỉ cần đọc ngay qua nó vì đặt tên phương pháp tốt.
Jimmy Hoffa

4
Mặt khác, có một phương thức cần n tham số cũng là một mùi mã và chỉ ra rằng phương thức đó có thể làm nhiều hơn một việc. Tương tự như vậy đối với việc có một phương thức khó đặt tên. Và nếu nó thực sự cần tất cả các tham số đó, chúng thường là một phần của một khái niệm lớn hơn, có thể và nên được đặt trong một lớp của chính nó. Tất nhiên, chúng ta có thể đưa ra một ví dụ tốt hơn là không sử dụng quy tắc này - quan điểm của tôi là nếu bạn thấy một phương pháp như vậy, hãy điều tra nó một cách xuyên suốt, có thể nó rất tệ theo một cách nào đó.
KL

28

Các phương pháp dài luôn xấu, nhưng đôi khi tốt hơn các phương pháp thay thế.


5
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ư "Phương pháp dài không bao giờ xấu, nhưng đôi khi còn tệ hơn các phương pháp thay thế". , làm thế nào câu trả lời này sẽ giúp người đọc chọn hai ý kiến ​​trái ngược nhau? Xem xét chỉnh sửa ing nó thành một hình dạng tốt hơn
gnat

9

Phương pháp dài là một mùi mã . Họ thường chỉ ra rằng có điều gì đó không đúng, nhưng đó không phải là quy tắc khó và nhanh. Thông thường, các trường hợp mà chúng được biện minh liên quan đến rất nhiều quy tắc kinh doanh nhà nước và khá phức tạp (như bạn đã tìm thấy).

Đối với câu hỏi khác của bạn, việc phân tách các khối logic thành các phương thức riêng biệt thường rất hữu ích, ngay cả khi chúng chỉ được gọi một lần. Nó giúp dễ dàng nhìn thấy logic mức cao hơn và có thể làm cho ngoại lệ xử lý sạch hơn một chút. Miễn là bạn không phải truyền hai mươi tham số để thể hiện trạng thái xử lý!


7

Phương pháp dài không phải lúc nào cũng xấu. Chúng thường là một dấu hiệu cho thấy có thể có một vấn đề.

Trên hệ thống tôi đang làm việc, chúng tôi có một nửa tá phương thức dài hơn 10.000 dòng. Một cái hiện dài 54.830 dòng. Và nó ổn.

Các hàm dài vô lý này rất đơn giản và được tự động tạo. Con quái vật dài 54.830 đó chứa dữ liệu chuyển động cực hàng ngày từ ngày 1 tháng 1 năm 1962 đến ngày 10 tháng 1 năm 2012 (bản phát hành cuối cùng của chúng tôi). Chúng tôi cũng phát hành một quy trình theo đó người dùng của chúng tôi có thể cập nhật tệp được tạo tự động đó. Tệp nguồn đó chứa dữ liệu chuyển động cực từ http://data.iers.org/products/214/14443/orig/eopc04_08_IAU2000.62-now , tự động dịch sang C ++.

Đọc trang web đó một cách nhanh chóng là không thể trong một cài đặt an toàn. Không có kết nối với thế giới bên ngoài. Tải xuống trang web dưới dạng bản sao cục bộ và phân tích cú pháp trong C ++ cũng không phải là một tùy chọn; phân tích cú pháp chậm , và điều này phải nhanh chóng. Tải xuống, tự động dịch sang C ++ và biên dịch: Bây giờ bạn có thứ gì đó nhanh. . Không có gì để tối ưu hóa. Đó là mã đường thẳng đơn giản, một câu lệnh gán sau một câu lệnh khác.)


6
"một tuyên bố chuyển nhượng khác" ... Tôi gọi đó là "tệp dữ liệu". Tại sao nó là mã?
Joachim Sauer

3
@JoachimSauer - Bởi vì phân tích tệp dữ liệu lớn trong thời gian chạy trong thiết lập Monte Carlo là một ý tưởng tồi. Một ý tưởng rất, rất xấu.
David Hammen

4
@DavidHammen: sau đó thực hiện nó tại thời điểm khởi tạo, giống như bạn đang buộc trình liên kết / trình tải của bạn phải làm. Hoặc viết tệp dữ liệu dưới dạng cấu trúc C trong tệp tiêu đề, thay vì mã C. Ít nhất thì trình tải sẽ tải dưới dạng khối dữ liệu.
Javier

3
@Javier: Ngay cả tại thời điểm khởi tạo, nó có thể là một ý tưởng rất tồi, ít nhất là trong một thiết lập Monte Carlo. Một mô phỏng chỉ mất vài phút để khởi tạo, nhưng chỉ vài giây để chạy ngược lại với việc có hàng chục ngàn lượt chạy trong một đêm. Thay đổi các tác vụ thời gian khởi tạo khóa để biên dịch các tác vụ thời gian sẽ khắc phục vấn đề đó. Chúng tôi đã thử một số kỹ thuật, bao gồm phương pháp cấu trúc dữ liệu được biên dịch. Nó chỉ không hoạt động hoặc sẽ rất khó để thực hiện công việc trong một số trường hợp (ví dụ, một mô hình trọng lực khổng lồ). Cách tiếp cận mã đường thẳng dễ tự động, dễ xác minh. Đó chỉ là mã xấu xí.
David Hammen

7
+1 câu chuyện thú vị. Tất nhiên, mã được tạo không phải là mã nguồn, vì vậy người ta có thể lập luận rằng điều này không 'phá vỡ' quy tắc. một giả định rằng trình tạo mã tự nó có các phương thức ngắn đẹp
jk.

7

Chúng ta hãy nói rằng có những cách tốt và xấu để phá vỡ một phương pháp dài. Phải "[giữ] phương pháp ngoài cùng trong đầu" là một dấu hiệu cho thấy bạn không phá vỡ nó theo cách tối ưu nhất, hoặc các đối tượng phụ của bạn được đặt tên kém. Về lý thuyết, có những trường hợp một phương pháp dài là tốt hơn. Trong thực tế, nó cực kỳ hiếm. Nếu bạn không thể tìm ra cách làm cho một phương thức ngắn hơn có thể đọc được, hãy nhờ ai đó xem lại mã của bạn và hỏi họ cụ thể về các ý tưởng về việc rút ngắn các phương thức.

Đối với nhiều vòng lặp gây ra hiệu suất giả định, không có cách nào để biết điều đó mà không đo. Nhiều vòng lặp nhỏ hơn có thể nhanh hơn đáng kể nếu điều đó có nghĩa là mọi thứ nó cần có thể nằm trong bộ đệm. Ngay cả khi có một hiệu suất thành công, nó thường không đáng kể để ủng hộ khả năng đọc.

Tôi sẽ nói rằng các phương thức dài thường dễ viết hơn , mặc dù chúng khó đọc hơn . Đó là lý do tại sao chúng sinh sôi nảy nở mặc dù không ai thích chúng. Không có gì sai khi lập kế hoạch từ đầu đến tái cấu trúc trước khi bạn kiểm tra nó.


1
"Không có gì sai với kế hoạch từ đầu để tái cấu trúc trước khi bạn kiểm tra nó." +1 cho điều đó. Hầu hết các IDE hiện nay đều có các công cụ tái cấu trúc giúp việc này trở nên cực kỳ dễ dàng. Nhưng có một phương pháp ngược lại khi bạn ủy thác mọi thứ cho các hàm không tồn tại và sau đó đi vào và điền vào các phương thức, nhưng tôi chưa bao giờ có thể viết mã theo cách đó, nhiều như tôi đã thử.
Tjaart

+1 cho "Phải" [giữ] phương pháp ngoài cùng trong đầu bạn "là một dấu hiệu cho thấy bạn không phá vỡ nó theo cách tối ưu nhất"
Michael Shaw

4

Các phương thức dài có thể tính toán và không gian hiệu quả hơn, có thể dễ dàng thấy logic hơn và dễ gỡ lỗi hơn. Tuy nhiên các quy tắc này chỉ thực sự áp dụng khi chỉ có một lập trình viên chạm vào mã đó. Mã sẽ là một nỗi đau để mở rộng nếu nó không phải là nguyên tử, về cơ bản, người tiếp theo sẽ phải bắt đầu lại từ đầu, sau đó gỡ lỗi và kiểm tra điều này sẽ tồn tại mãi mãi vì nó không sử dụng bất kỳ mã tốt nào đã biết.


34
Luôn có ít nhất hai lập trình viên tham gia: "bạn" và "bạn, ba tuần kể từ bây giờ".
Joachim Sauer

3

Có một cái gì đó mà chúng tôi gọi là Phân rã chức năng ngụ ý chia nhỏ các phương thức dài hơn của bạn thành các phương thức nhỏ hơn bất cứ khi nào có thể. Như bạn đã đề cập rằng phương pháp của bạn liên quan đến việc sắp xếp / lọc thì tốt hơn bạn nên có các phương thức hoặc hàm riêng biệt cho các tác vụ này.

Chính xác, phương pháp của bạn chỉ nên tập trung vào perforimng 1 nhiệm vụ.

Và nếu nó cần gọi một phương thức khác, một số lý do thì hãy làm như vậy nếu không hãy tiếp tục với phương thức bạn đang viết. Ngoài ra đối với pruposes dễ đọc, bạn có thể thêm ý kiến. Thông thường, các lập trình viên sử dụng các nhận xét nhiều dòng (/ ** / trong C, C ++, C # và Java) cho các mô tả phương thức và sử dụng các nhận xét một dòng (// trong C, C ++, C # và Java). Ngoài ra còn có các công cụ tài liệu tốt cũng có sẵn để dễ đọc mã hơn (ví dụ: JavaDoc ). Bạn cũng có thể xem xét các nhận xét dựa trên XML nếu bạn là nhà phát triển .Net.

Vòng lặp làm ảnh hưởng đến hiệu suất chương trình và có thể gây ra chi phí ứng dụng nếu không được sử dụng đúng cách. Ý tưởng là thiết kế thuật toán của bạn sao cho bạn sử dụng các vòng lặp lồng nhau càng ít càng tốt.


Còn được gọi là "Một chức năng nên làm MỘT ĐIỀU."
lorddev

3

Hoàn toàn ổn khi viết các hàm dài. Nhưng nó thay đổi trên bối cảnh cho dù bạn thực sự cần hay không. Ví dụ, một số đại số tốt nhất được thể hiện tốt nhất khi nó là một hoa mẫu đơn. Mặt khác, một tỷ lệ lớn các thói quen trong các chương trình hướng đối tượng sẽ là các thói quen truy cập, sẽ rất ngắn. Một số o của các quy trình xử lý dài có các trường hợp chuyển đổi dài, nếu các điều kiện có thể được tối ưu hóa thông qua các phương thức điều khiển bảng.

Có một cuộc thảo luận ngắn tuyệt vời trong Code Complete 2 về độ dài của các thói quen.

Độ dài tối đa 497 tốt nhất về mặt lý thuyết thường được mô tả là một hoặc hai trang liệt kê chương trình, từ 66 đến 132 dòng. Các chương trình hiện đại có xu hướng có khối lượng các thói quen cực kỳ ngắn xen lẫn với một vài thói quen dài hơn.

Hàng thập kỷ bằng chứng nói rằng các thói quen có độ dài như vậy không dễ bị lỗi hơn các thói quen ngắn hơn. Đặt các vấn đề như độ sâu của lồng, số lượng biến và các cân nhắc liên quan đến độ phức tạp khác chỉ ra 535 độ dài của thói quen thay vì áp đặt độ dài

Nếu bạn muốn viết các thói quen dài hơn khoảng 200 dòng, hãy cẩn thận. Không có nghiên cứu nào báo cáo giảm chi phí, giảm tỷ lệ lỗi hoặc cả hai với các thói quen lớn hơn được phân biệt giữa các kích thước lớn hơn 200 dòng và bạn buộc phải chạy đến giới hạn dễ hiểu hơn khi bạn vượt qua 200 dòng mã. 536 hạn chế mỗi se.


2

Một phiếu khác cho rằng nó hầu như luôn luôn sai. Tôi tìm thấy hai trường hợp cơ bản trong đó là câu trả lời thích hợp, mặc dù:

1) Một phương thức về cơ bản chỉ gọi một loạt các phương thức khác và không có công việc thực sự nào. Bạn có một quy trình mất 50 bước để thực hiện, bạn có được một phương thức với 50 cuộc gọi trong đó. Thường không có gì để đạt được bằng cách cố gắng phá vỡ điều đó.

2) Công văn. Thiết kế OOP đã loại bỏ hầu hết các phương pháp như vậy nhưng các nguồn dữ liệu đến tự bản chất chỉ là dữ liệu và do đó không thể tuân theo các nguyên tắc OOP. Nó không thực sự bất thường khi có một số loại thói quen điều phối trong mã xử lý dữ liệu.

Tôi cũng sẽ nói rằng người ta thậm chí không nên xem xét câu hỏi khi xử lý các công cụ tự động. Không ai cố gắng hiểu mã tự động làm gì, vấn đề không phải là một iota cho dù con người có dễ hiểu hay không.


1
Quá trình 50 bước có thể được tóm tắt thành nhiều nhóm thông qua. Bước 1 - 9 là kiểm tra tham số, vì vậy hãy tạo một phương thức mới gọi là kiểm tra tham số. (Tôi chắc chắn có một số ví dụ trong đó điều này là không thể. Tôi sẽ quan tâm đến việc xem một ví dụ).
Sixty feetersdude

@sixtyfootersdude: Chắc chắn, bạn có thể phá vỡ nó. Tôi đã không nói điều đó là không thể, tôi đã nói rằng không có gì có thể đạt được bằng cách phá vỡ nó. Trong khi chưa được 50 bước, tôi đã đạt được điều tương tự: Tạo ra một thế giới trò chơi. # 1 tạo ra thế giới trống rỗng, # 2 tạo ra địa hình và sau đó là một loạt các bước sau đó xoa bóp nó bằng cách này hay cách khác.
Loren Pechtel

& sixty feetersdude: Thật đáng kinh ngạc khi bạn biết mã mà bạn chưa từng thấy và cách cải thiện nó.
gnasher729

2

Tôi muốn giải quyết ví dụ bạn đã đưa ra:

Ví dụ, tôi có một số khung nhìn Django xử lý một chút các đối tượng trước khi gửi chúng tới khung nhìn, một phương thức dài là 350 dòng mã. Tôi có mã được viết để nó xử lý các tham số - sắp xếp / lọc bộ truy vấn, sau đó từng chút một xử lý trên các đối tượng mà truy vấn của tôi đã trả về.

Tại công ty của tôi, dự án lớn nhất của chúng tôi được xây dựng trên Django và chúng tôi cũng có các chức năng xem dài (nhiều hơn 350 dòng). Tôi cho rằng chúng ta không cần phải lâu như vậy và họ đang làm tổn thương chúng ta.

Các hàm xem này đang thực hiện rất nhiều công việc liên quan lỏng lẻo nên được trích xuất vào mô hình, các lớp của trình trợ giúp hoặc các hàm trợ giúp. Ngoài ra, chúng tôi cuối cùng sử dụng lại các quan điểm để làm những việc khác nhau, thay vào đó nên được chia thành các quan điểm gắn kết hơn.

Tôi nghi ngờ quan điểm của bạn có đặc điểm tương tự. Trong trường hợp của tôi, tôi biết nó gây ra vấn đề và tôi đang làm việc để thay đổi. Nếu bạn không đồng ý rằng nó gây ra sự cố, thì bạn không cần phải sửa nó.


2

Tôi không biết nếu ai đó đã đề cập đến điều này, nhưng một trong những lý do khiến các phương pháp dài là xấu là vì chúng thường liên quan đến một số mức độ trừu tượng khác nhau. Bạn có các biến vòng lặp và tất cả các loại điều xảy ra. Hãy xem xét chức năng hư cấu:

function nextSlide() {
  var content = getNextSlideContent();
  hideCurrentSlide();
  var newSlide = createSlide(content);
  setNextSlide(newSlide);
  showNextSlide();
}

Nếu bạn đang thực hiện tất cả hoạt hình, tính toán, truy cập dữ liệu, v.v. trong chức năng đó thì đó sẽ là một mớ hỗn độn. Hàm nextSlide () giữ một lớp trừu tượng nhất quán (hệ thống trạng thái trượt) và bỏ qua các lớp khác. Điều này làm cho mã có thể đọc được.

Nếu bạn phải liên tục bước vào các phương thức nhỏ hơn để xem những gì họ làm thì việc thực hiện phân chia chức năng đã thất bại. Chỉ vì mã bạn đang đọc không làm những điều hiển nhiên trong các phương thức con không có nghĩa là các phương thức con là một ý tưởng tồi, chỉ là nó được thực hiện không chính xác.

Khi tôi tạo các phương thức, tôi thường kết thúc việc chia chúng thành các phương thức nhỏ hơn như là một chiến lược phân chia và chinh phục. Một phương pháp như

   if (hasMoreRecords()) { ... }

chắc chắn là dễ đọc hơn

if (file.isOpen() && i < recordLimit && currentRecord != null) { ... } 

Đúng?

Tôi đồng ý về các tuyên bố tuyệt đối là xấu và cũng đồng ý rằng thường thì một phương pháp dài là xấu.


1

Câu chuyện có thật. Tôi đã từng gặp một phương pháp dài hơn hai nghìn dòng. Phương pháp này có các vùng mô tả những gì nó đang làm trong các vùng đó. Sau khi đọc qua một vùng, tôi quyết định thực hiện một phương pháp trích xuất tự động, đặt tên theo tên vùng. vào thời điểm tôi hoàn thành, phương thức này không có gì ngoài 40 cuộc gọi phương thức gồm khoảng năm mươi dòng mỗi phương thức và tất cả đều hoạt động như nhau.

Những gì quá lớn là chủ quan. Đôi khi, một phương thức không thể bị phá vỡ hơn bất kỳ phương pháp nào hiện tại. Nó giống như viết một cuốn sách. Hầu hết mọi người đồng ý rằng các đoạn văn dài thường nên được chia. Nhưng đôi khi, chỉ có một ý tưởng và việc phân tách nó gây ra nhiều nhầm lẫn hơn là do độ dài của đoạn đó gây ra.


0

Quan điểm của một phương pháp là giúp giảm sự hồi quy mã. Một phương thức nên có một chức năng cụ thể mà nó chịu trách nhiệm. Nếu bạn kết thúc việc xử lý lại mã ở nhiều nơi, bạn sẽ gặp rủi ro mã có kết quả không mong muốn nếu thông số kỹ thuật phần mềm được thiết kế để giải quyết.

Đối với một phương thức có 350 dòng sẽ gợi ý rằng rất nhiều nhiệm vụ mà nó đang thực hiện được sao chép ở nơi khác vì điều đó là bất thường khi yêu cầu một lượng lớn mã như vậy để thực hiện một nhiệm vụ chuyên biệt.


Giúp giảm mã ?
Avner Shahar-Kashtan

@ AvnerShahar-Kashtan, có lẽ anh ta có nghĩa là "nhân đôi" :-)
Péter Török

0

Đó thực sự không phải là những phương pháp lâu dài mà thực tiễn tồi, nó càng khiến chúng trở nên tồi tệ.

Ý tôi là hành động thực tế tái cấu trúc mẫu của bạn từ:

varaible_1 = 0
variable_2 = 0
for object in queryset :
     if object.condition_condition_a and variable_2 > 0 :
     variable 1+= 1
     .....
     ...    
     . 
      more conditions to alter the variables

return queryset, and context 

đến

Status status = new Status();
status.variable1 = 0;
status.variable2 = 0;
for object in queryset :
     if object.condition_condition_a and status.variable2 > 0 :
     status.variable1 += 1
     .....
     ...    
     . 
      more conditions to alter the variables (status)

return queryset, and context 

và sau đó để

class Status {
    variable1 = 0;
    variable2 = 0;

    void update(object) {
        if object.condition_condition_a and variable2 > 0 {
            variable1 += 1
        }
    }
};

Status status = new Status();
for object in queryset :
     status.update(object);
     .....
     ...    
     . 
      more conditions to alter the variables (status)

return queryset, and context 

bây giờ bạn đang trên đường không chỉ là một phương pháp ngắn hơn nhiều mà còn là một phương pháp dễ sử dụng và dễ hiểu hơn nhiều.


0

Tôi nghĩ rằng thực tế là phương pháp này rất dài để kiểm tra, nhưng chắc chắn không phải là một mô hình chống tức thời. Điều lớn để tìm kiếm trong các phương pháp lớn là rất nhiều lồng nhau. Nếu bạn có

foreach(var x in y)
{
    //do ALL the things
    //....
}

và phần thân của vòng lặp không quá cục bộ (nghĩa là bạn có thể gửi nó ít hơn 4 tham số), thì có lẽ tốt hơn là chuyển đổi nó thành:

foreach(var x in y)
{
    DoAllTheThings(x);
}
...
void DoAllTheThings(object x)
{
    //do ALL the things
    //....
}

Đổi lại, điều này có thể làm giảm đáng kể độ dài của một chức năng. Ngoài ra, hãy đảm bảo tìm mã trùng lặp trong hàm và di chuyển mã đó sang một hàm riêng

Cuối cùng, một số phương pháp chỉ dài và phức tạp và bạn không thể làm gì được. Một số vấn đề cần giải pháp mà không cho vay để dễ viết mã. Ví dụ, phân tích cú pháp theo một ngữ pháp phức tạp có thể tạo ra các phương thức thực sự dài mà bạn thực sự không thể làm được gì nhiều mà không làm cho nó tồi tệ hơn.


0

Sự thật là, nó phụ thuộc. Như đã đề cập, nếu mã không tách rời mối quan tâm và cố gắng thực hiện mọi thứ trong một phương thức thì đó là một vấn đề. Việc tách mã thành nhiều mô-đun giúp đọc mã dễ dàng hơn, cũng như viết mã (bởi nhiều lập trình viên). Tuân thủ một mô-đun (lớp) cho mỗi tệp nguồn là một ý tưởng tốt để bắt đầu.

Thứ hai, khi nói đến chức năng / thủ tục:

void setDataValueAndCheckForRange(Data *data) {/*code*/} 

là một phương pháp tốt nếu nó kiểm tra phạm vi chỉ "Dữ liệu". Đây là một phương thức BAD khi cùng một phạm vi áp dụng cho nhiều hàm (ví dụ về mã xấu):

void setDataValueAndCheckForRange(Data *data){ /*code */}
void addDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}
void subDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}
void mulDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}

Điều này phải được tái cấu trúc để:

bool isWithinRange(Data *d){ /*code*/ }
void setDataValue(Data *d) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void addDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void subDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void mulDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 

Mã sử ​​dụng lại càng nhiều càng tốt. Và điều đó là có thể khi mỗi chức năng của chương trình của bạn là ĐƠN GIẢN (không nhất thiết phải dễ dàng).

HỎI: MOUNTAIN bao gồm các hạt nhỏ của trái đất. Đại dương được tạo thành từ những giọt nước nhỏ .. (- Sivananda)


0

Các phương pháp dài có xu hướng "xấu" trong các ngôn ngữ bắt buộc thiên về các tuyên bố, tác dụng phụ và khả năng biến đổi, chính xác là vì các tính năng này làm tăng độ phức tạp và do đó có lỗi.

Trong các ngôn ngữ lập trình chức năng, vốn ưa thích các biểu thức, độ tinh khiết và tính bất biến, có ít lý do để quan tâm hơn.

Trong các ngôn ngữ chức năng và mệnh lệnh giống nhau, tốt nhất là luôn đưa các đoạn mã có thể sử dụng lại thành các thói quen cấp cao nhất, nhưng trong các ngôn ngữ chức năng hỗ trợ phạm vi từ vựng với các hàm lồng nhau, v.v. Hàm -level (phương thức) hơn là chia chúng thành các hàm cấp cao nhất khác.


Tuy nhiên, trong các ngôn ngữ chức năng, các phương thức dài ít phổ biến hơn nhiều.
itbruce
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.