Một thực hành mã tốt khi nào để tạo một hàm / phương thức cho các đoạn mã lặp lại nhỏ là gì?


12

Nhiều lần trong suốt quá trình viết các chương trình lớn hơn, tôi đã đặt câu hỏi sau bao nhiêu bản sao và dán có ý nghĩa gì khi đặt mã vào một hàm hoặc phương thức và quy tắc tốt là gì? Tôi đã sử dụng quy tắc ngón tay cái gồm bốn dòng hoặc lớn hơn và xuất hiện nhiều hơn hai lần, sau đó tôi tạo một hàm / phương thức đơn giản chứa mã đó. Bạn có thể nghĩ về một thực hành tốt hơn hoặc cung cấp bất kỳ con trỏ? Đây là nhiều hơn một câu hỏi mẫu thiết kế chung chứ không phải là một câu hỏi cụ thể về ngôn ngữ.

Câu trả lời:


29

Tôi sử dụng các chức năng một phần như là một cách để ghi lại mã. Gọi một hàm với một tên có ý nghĩa giúp dễ hiểu mã hơn. Trong một số trường hợp, ngay cả một hàm với một dòng duy nhất cũng có ý nghĩa.

Ví dụ, trong "Mã sạch", Robert C. Martin đưa ra ví dụ sau: Bạn muốn xem cái nào hơn? Điều này:

// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&
    (employee.age > 65))

Hay cái này?

if (employee.isEligibleForFullBenefits())

Tôi không luôn đồng ý với anh ta, nhưng trong trường hợp này thì có. Mã phải được đọc, không chỉ khi bạn viết nó và bạn biết mọi chi tiết, mà cả lúc 9 giờ tối khi bạn phải sửa lỗi trong mã của người khác. Nhìn chằm chằm vào một điều kiện dài và cố gắng tìm ra tất cả các tiêu cực kép không được khuyến khích. Nếu bạn chỉ có thể đặt tên cho nó (không chỉ các điều kiện, mà mỗi đoạn mã bạn viết), nó sẽ trở nên đơn giản hơn rất nhiều.

Tôi chưa bao giờ hối hận khi đặt một cái gì đó vào một chức năng, và nếu bạn lo lắng về hiệu suất, thì hãy cấu hình trước.


2
Thực hiện theo cách thực hành đơn giản này cuối cùng sẽ cho phép bạn viết mã ứng dụng ở mức tương đối cao. Các hàm nhỏ được thu thập thành các lớp nhỏ và chẳng mấy chốc bạn sẽ chuyển đổi các thông số chức năng thành mã gần như từng chữ.
kevin cline

11
Tôi thích ví dụ này. Đột nhiên bạn không cần bình luận đó nữa. Đây là một quy tắc của ngón tay cái : nếu bình luận của bạn có thể được chuyển đổi thành một biến hoặc tên hàm, hãy làm điều đó!
Karoly Horvath

Tôi đồng ý với omrib ở đây, thường là về việc làm sạch mã - điều gì làm cho nó dễ đọc hơn bất kỳ quy tắc ngón tay cái nào khác. Nếu tôi cuối cùng sử dụng lại một cái gì đó, tôi sẽ trích xuất nó thành một phương thức. Tuy nhiên, IDE và công cụ của tôi thường giúp với điều đó, vì vậy thật dễ dàng và nhanh chóng để làm.
Travis

+1 Loại mã này thậm chí có thể nhanh hơn, ít nhất là nếu nó cần phải là JIT-ed - bạn chỉ trả tiền cho những gì bạn sử dụng.
Công việc

1
còn được gọi là ý định tiết lộ tên .
rwong

13

Có một sự hiểu lầm phổ biến rằng các lệnh gọi hàm chỉ nên được thực hiện để tránh các phân đoạn mã lặp đi lặp lại. Nguyên tắc nhỏ của tôi là bất kỳ đơn vị công việc logic nào cũng phải được tạo thành một hàm, ngay cả khi nó chỉ được sử dụng ở một nơi duy nhất. Điều này thường dẫn đến khả năng đọc tốt hơn và cho phép bạn viết mã tự viết tài liệu, trong đó tên hàm thay thế các bình luận và bạn không cần phải viết thêm các bình luận giải thích những gì bạn đang làm.


1
Xem những gì lập trình viên sẽ đi vào chiều dài để tránh viết bình luận?
Alger

1
@ Alger như họ nên
MatrixFrog

6

Nếu nó được sử dụng trong nhiều hơn một nơi, và

  • nó có khả năng thay đổi, hoặc
  • thật khó để làm đúng

sau đó làm cho nó một chức năng hoặc phương thức. Theo kinh nghiệm của tôi, các đoạn mã lặp đi lặp lại sẽ tự nhiên rơi vào một trong các loại này (thường là loại đầu tiên, nhưng sau đó các danh mục trùng nhau rất nhiều;). Tất nhiên, bất cứ thứ gì phải có trong giao diện cũng là một chức năng / phương thức riêng.


3
Câu hỏi là: tại sao bạn không viết một hàm cho một đoạn mã thường được lặp đi lặp lại ngay cả khi nó không khó để có được quyền hoặc có khả năng thay đổi? (Quy tắc ngón tay cái của tôi: nếu nó lặp đi lặp lại và lâu hơn sau đó gọi một hàm, hãy biến nó thành một hàm)
Winston Ewert

Tôi sẽ đi xa hơn nữa. Là một lập trình viên, bạn phải loại bỏ tất cả các loại lặp lại . Cho dù đó là trong cơ sở dữ liệu (bình thường hóa), một số thử nghiệm thủ công (thay thế nó bằng các thử nghiệm đơn vị) hoặc triển khai (tự động hóa nó).
Karoly Horvath

@Winston: điều đó phụ thuộc vào ngôn ngữ được sử dụng. Không phải mọi cấu trúc có thể được nắm bắt một cách tự nhiên như một hàm, hàm có thể chiếm nhiều không gian hơn mã gốc (nghĩ C và trả về bằng con trỏ), các lệnh gọi hàm có thể phải chịu chi phí.
Fred Foo

@larsman, tôi tò mò không biết bạn đang đề cập đến điều gì bởi "(nghĩ C và trả về bằng con trỏ)". Nhưng những gì bạn đang nói là những gì tôi đã cố gắng để có được với quy tắc của tôi. Gọi hàm phải dễ dàng hơn (tức là tự nhiên bắt và chiếm ít không gian hơn) sau đó thực hiện các nội dung của hàm.
Winston Ewert

Nếu một đoạn mã tính toán nhiều giá trị, giả sử float x, int ydouble densitysau đó thiết lập các phép tính đó dưới dạng hàm C có thể khó hơn là chỉ lặp lại mã, vì bạn phải nghĩ ra cách để loại bỏ cả ba giá trị. Nếu các tính toán lặp đi lặp lại là tầm thường, đôi khi tốt hơn là cứ để chúng bên trong.
Fred Foo

4

Hầu như luôn luôn, đặc biệt là nếu mỗi bản sao đại diện cho cùng một hoạt động theo quan điểm khái niệm. Nếu nó được thực hiện theo cùng một cách, nhưng trên các loại khác nhau, hãy thực hiện chung.

Lý do duy nhất không làm như vậy mà tôi có thể nghĩ đến là một trong những bảo trì: đôi khi có thể thuận tiện hơn để tránh tạo ra sự phụ thuộc giữa những thứ riêng biệt, thậm chí với chi phí trùng lặp.


Cẩn thận với việc gõ vịt, nếu bây giờ việc thực hiện là tương tự, nhưng chức năng là khác nhau hiệu quả, sau đó hợp nhất hai làm cho nó khó chịu như địa ngục để phân chia lại. Đặc biệt là trong các ngôn ngữ có hỗ trợ IDE kém (hey, tôi làm việc trong C ++ ...)
Matthieu M.

Mặt khác, bằng cách tách chúng ra, bạn có hai chức năng thực hiện cùng một chức năng để kiểm tra, một nửa khả năng mã của bạn được thực thi, hai vị trí trong đó cùng một lỗi có thể xuất hiện và bạn phải nhớ sửa lỗi trong đó lỗi chưa được phát hiện. Tôi vẫn muốn làm việc trong C ++, mặc dù hỗ trợ IDE kém ;-)
Nicola Musatti

1

Tìm kiếm " tái cấu trúc " sẽ đưa bạn đến nhiều nguồn lực cho "các thực tiễn tốt nhất" trong ngành cho quy trình rất phổ biến này. Bài báo hơi nổi tiếng, Một lần và Chỉ một lần là một tài liệu tham khảo lịch sử tuyệt vời giải thích những gì một số người coi là "thực tiễn tốt nhất" cho những mối quan tâm được nêu ra bởi câu hỏi của bạn. Ngoài ra, khái niệm thậm chí còn chung hơn được gọi là Đừng lặp lại chính mình (DRY) . Để có một bộ câu trả lời thực sự sâu sắc cho câu hỏi của bạn, hãy đọc tác phẩm cổ điển tuyệt vời của Martin Fowler , Tái cấu trúc: Cải thiện Thiết kế của Mã hiện tại , bao gồm một số lời khuyên nổi tiếng nhất để tái cấu trúc , đó là những gì bạn đang cố gắng thực hiện bằng trực giác !


0

Nếu mã được lặp lại chính xác ở nhiều nơi và phần lặp lại sẽ không thay đổi trong tương lai gần thì tôi sẽ tách nó thành một hàm.


1
nếu nó sẽ thay đổi, thậm chí nhiều lý do để tái cấu trúc nó. Sau đó, bạn sẽ chỉ phải thay đổi một lần
SHug

0

Điều đó phụ thuộc vào bản chất của sự gắn kết của mã lặp lại. Nếu phần mã lặp đi lặp lại đang thực hiện một chức năng cụ thể, thì đó là một ứng cử viên tuyệt vời để được tạo thành một phương thức, một phần vì nguyên tắc DRY , một phần vì nếu chức năng cần được tối ưu hóa hoặc sửa chữa, thì chỉ có một phần mã để đối phó với.

Nếu sự liên kết là ngẫu nhiên, tốt hơn là lặp lại mã hơn là biến nó thành một phương thức. Nếu bạn cần thêm một cái gì đó vào giữa một trong các chuỗi mã để đáp ứng một trong những cách sử dụng đoạn mã đó, nếu đó là trong một phương thức, thay đổi bạn thực hiện có thể ảnh hưởng đến việc sử dụng phương thức đó.

Xem bài viết Wikipedia về khái niệm gắn kết mã .


Nếu hiệp hội có vẻ trùng hợp, có khả năng hai quy trình có chung một ý tưởng và có lẽ bạn nên khám phá xem hai quy trình có thực sự là hai khía cạnh của cùng một điều hay không. Thường xuyên hơn không, nó là.
Nói dối Ryan

0

Bạn phải phân biệt giữa các hàm theo nghĩa lập trình có cấu trúc và các phương thức của một lớp.

Trong ví dụ của bạn, những gì bạn đã thể hiện là một phương thức như vậy không nên được mã hóa theo dòng.

bạn có thể phải xác thực một chuỗi để xem đó có phải là số hay không, trong trường hợp này, bạn sử dụng hàm và hầu hết các câu trả lời trước được áp dụng.

Sự khác biệt này là đặc biệt quan trọng trong các dự án lớn.

Càng nhiều càng tốt, cố gắng tách các quy tắc kinh doanh (đó là các phương thức) khỏi các thuật toán điện toán (đó là các hàm lập trình thuần tú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.