Có hướng dẫn về bao nhiêu tham số một chức năng nên chấp nhận?


114

Tôi đã nhận thấy một vài chức năng tôi làm việc có 6 tham số trở lên, trong khi ở hầu hết các thư viện tôi sử dụng, rất hiếm khi tìm thấy một hàm có nhiều hơn 3.

Thông thường rất nhiều các tham số phụ này là các tùy chọn nhị phân để thay đổi hành vi chức năng. Tôi nghĩ rằng một số chức năng tham số có lẽ nên được tái cấu trúc. Có một hướng dẫn cho số lượng là quá nhiều?


4
@Ominus: Ý tưởng là bạn muốn giữ cho các lớp học của bạn tập trung. Các lớp tập trung thường không có nhiều phụ thuộc / thuộc tính do đó bạn có ít tham số hơn cho hàm tạo. Một số từ buzz mọi người ném vào thời điểm này là sự gắn kết caonguyên tắc trách nhiệm duy nhất . Nếu bạn cảm thấy rằng những điều này không bị vi phạm và vẫn cần nhiều thông số, hãy cân nhắc sử dụng mẫu Builder.
c_maker

2
Chắc chắn không làm theo ví dụ về MPI_Sendrecv () , có 12 tham số!
chrisaycock

6
Dự án tôi hiện đang làm việc sử dụng một khung nhất định, trong đó các phương thức có hơn 10 tham số là phổ biến. Tôi đang gọi một phương thức cụ thể với 27 tham số ở một vài nơi. Mỗi lần tôi nhìn thấy nó tôi chết một chút bên trong.
perp

3
Không bao giờ thêm các công tắc boolean để thay đổi hành vi chức năng. Thay thế chức năng thay thế. Phá vỡ các hành vi phổ biến thành các chức năng mới.
kevin cline

2
@Ominus Gì? Chỉ có 10 thông số? Không có gì, cần nhiều hơn nữa . : D
maaartinus

Câu trả lời:


106

Tôi chưa bao giờ thấy một hướng dẫn, nhưng theo kinh nghiệm của tôi, một hàm có nhiều hơn ba hoặc bốn tham số chỉ ra một trong hai vấn đề:

  1. Các chức năng đang làm quá nhiều. Nó nên được chia thành nhiều chức năng nhỏ hơn, mỗi chức năng có một bộ tham số nhỏ hơn.
  2. Có một đối tượng khác đang trốn trong đó. Bạn có thể cần tạo một đối tượng hoặc cấu trúc dữ liệu khác bao gồm các tham số này. Xem bài viết này trên mẫu Đối tượng tham số để biết thêm thông tin.

Thật khó để nói những gì bạn đang nhìn mà không có thêm thông tin. Rất có thể là cấu trúc lại mà bạn cần làm là chia hàm thành các hàm nhỏ hơn được gọi từ cha mẹ tùy thuộc vào các cờ hiện đang được truyền cho hàm.

Có một số lợi ích tốt để có được bằng cách này:

  • Nó làm cho mã của bạn dễ đọc hơn. Cá nhân tôi thấy việc đọc một "danh sách quy tắc" được tạo thành từ một ifcấu trúc gọi rất nhiều phương thức với các tên mô tả dễ dàng hơn nhiều so với một cấu trúc thực hiện tất cả trong một phương thức.
  • Đó là đơn vị nhiều hơn có thể kiểm tra. Bạn đã chia vấn đề của mình thành nhiều nhiệm vụ nhỏ hơn rất đơn giản. Bộ sưu tập thử nghiệm đơn vị sau đó sẽ được tạo thành từ một bộ thử nghiệm hành vi để kiểm tra các đường dẫn thông qua phương thức chính và một bộ các thử nghiệm nhỏ hơn cho từng thủ tục riêng lẻ.

5
Trừu tượng tham số đã được biến thành một mẫu thiết kế? Điều gì xảy ra nếu bạn có 3 lớp tham số. Bạn có thêm 9 phương thức quá tải để xử lý các kết hợp tham số khác nhau có thể không? Nghe có vẻ như một vấn đề khai báo tham số O (n ^ 2) khó chịu. Đợi đã, bạn chỉ có thể kế thừa 1 lớp trong Java / C #, do đó sẽ yêu cầu thêm một số lớp sinh học (có thể thêm một số lớp con) để làm cho nó hoạt động trong thực tế. Xin lỗi, tôi không bị thuyết phục. Bỏ qua các cách tiếp cận biểu cảm hơn, một ngôn ngữ có thể mang lại lợi ích cho sự phức tạp chỉ cảm thấy sai.
Evan Plaice

Trừ khi bạn sử dụng mẫu Đối tượng mẫu để đóng gói các biến trong một thể hiện đối tượng và truyền nó dưới dạng tham số. Điều đó làm việc cho việc đóng gói nhưng nó có thể hấp dẫn để tạo ra các lớp các biến khác nhau chỉ để thuận tiện cho việc đơn giản hóa định nghĩa phương thức.
Evan Plaice

@EvanPlaice Tôi không nói rằng bạn phải sử dụng mẫu đó bất cứ khi nào bạn có nhiều hơn một tham số - bạn hoàn toàn chính xác, điều đó thậm chí còn nhanh hơn cả danh sách đầu tiên. Có thể có trường hợp bạn thực sự cần một số lượng lớn các tham số và nó không hoạt động để bọc chúng trong một đối tượng. Tôi chưa gặp trường hợp nào trong phát triển doanh nghiệp không thuộc một trong hai nhóm mà tôi đã đề cập trong câu trả lời của mình - điều đó không nói rằng một người không tồn tại mặc dù.
Michael K

@MichaelK Nếu bạn chưa bao giờ sử dụng chúng, hãy thử googling 'trình khởi tạo đối tượng'. Đó là một cách tiếp cận khá mới lạ làm giảm đáng kể nồi hơi định nghĩa. Về lý thuyết, bạn có thể loại bỏ các hàm tạo, tham số của lớp và quá tải tất cả trong một lần chụp. Trong thực tế, thông thường, tốt nhất là duy trì một nhà xây dựng chung và dựa vào cú pháp 'trình khởi tạo đối tượng' cho phần còn lại của các thuộc tính tối nghĩa / thích hợp. IMHO, đây là lần gần nhất bạn có được tính biểu cảm của các ngôn ngữ được gõ động trong một ngôn ngữ được nhập tĩnh.
Evan Plaice

@Evain Plaice: Từ khi nào các ngôn ngữ được gõ động biểu hiện?
ThomasX

41

Theo "Mã sạch: Cẩm nang về thủ công phần mềm linh hoạt", số không là lý tưởng, một hoặc hai là chấp nhận được, ba trong trường hợp đặc biệt và bốn hoặc nhiều hơn, không bao giờ!

Những lời của tác giả:

Số lượng đối số lý tưởng cho một hàm là 0 (niladic). Tiếp đến là một (monadic), theo sát bởi hai (dyadic). Nên tránh ba đối số (bộ ba) nếu có thể. Hơn ba (polyadic) đòi hỏi sự biện minh rất đặc biệt và sau đó không nên sử dụng.

Trong cuốn sách này có một chương chỉ nói về các chức năng trong đó các tham số được thảo luận lớn, vì vậy tôi nghĩ cuốn sách này có thể là một hướng dẫn tốt về số lượng tham số bạn cần.

Theo ý kiến ​​cá nhân của tôi, một tham số tốt hơn không ai bởi vì tôi nghĩ rõ ràng hơn những gì đang xảy ra.

Ví dụ, theo tôi, lựa chọn thứ hai tốt hơn bởi vì rõ ràng hơn phương thức đang xử lý là gì:

LangDetector detector = new LangDetector(someText);
//lots of lines
String language = detector.detectLanguage();

so với

LangDetector detector = new LangDetector();
//lots of lines
String language = detector.detectLanguage(someText);

Về rất nhiều tham số, đây có thể là dấu hiệu cho thấy một số biến có thể được nhóm thành một đối tượng hoặc trong trường hợp này, rất nhiều booleans có thể biểu thị rằng hàm / phương thức đang làm nhiều hơn một điều đó, và trong trường hợp này, tốt hơn là tái cấu trúc mỗi một trong những hành vi này trong một chức năng khác nhau.


8
"ba trong trường hợp đặc biệt và bốn hoặc nhiều hơn, không bao giờ!" BS. Làm thế nào về Matrix.Create (x1, x2, x3, x4, x5, x6, x7, x8, x9); ?
Lukasz Madon

71
Không là lý tưởng? Làm thế quái nào các chức năng có được thông tin? Biến toàn cục / cá thể / tĩnh / gì? KINH QUÁ.
Peter C

9
Đó là một ví dụ tồi tệ. Câu trả lời rõ ràng là : String language = detectLanguage(someText);. Trong một trong các trường hợp của bạn, bạn đã vượt qua cùng một số lượng đối số chính xác, điều đó xảy ra là bạn đã chia thực thi hàm thành hai vì ngôn ngữ kém.
Matthieu M.

8
@lukas, bằng các ngôn ngữ hỗ trợ cấu trúc lạ mắt như mảng hoặc (thở hổn hển!) danh sách, làm thế nào về Matrix.Create(input);nơi inputlà, nói, một NET IEnumerable<SomeAppropriateType>? Bằng cách đó, bạn cũng không cần quá tải riêng khi bạn muốn tạo ma trận chứa 10 phần tử thay vì 9.
CVn

9
Không có đối số nào là "lý tưởng" là một crock và một lý do tôi nghĩ Clean Code bị đánh giá quá cao.
user949300

24

Nếu các lớp miền trong ứng dụng được thiết kế chính xác, số lượng tham số mà chúng ta truyền vào một hàm sẽ tự động giảm - vì các lớp biết cách thực hiện công việc của chúng và chúng có đủ dữ liệu để thực hiện công việc của chúng.

Ví dụ: giả sử bạn có một lớp người quản lý yêu cầu lớp 3 hoàn thành các bài tập.

Nếu bạn mô hình chính xác,

3rdGradeClass.finishHomework(int lessonId) {
    result = students.assignHomework(lessonId, dueDate);
    teacher.verifyHomeWork(result);
}

Cái này đơn giản.

Nếu bạn không có mô hình chính xác, phương pháp sẽ như thế này

Manager.finishHomework(grade, students, lessonId, teacher, ...) {
    // This is not good.
}

Mô hình đúng luôn giảm các tham số hàm giữa các lệnh gọi phương thức vì các hàm đúng được ủy quyền cho các lớp riêng của chúng (Trách nhiệm đơn) và chúng có đủ dữ liệu để thực hiện công việc của chúng.

Bất cứ khi nào tôi thấy số lượng tham số tăng lên, tôi kiểm tra mô hình của mình để xem liệu tôi có thiết kế mô hình ứng dụng của mình một cách chính xác không.

Mặc dù có một số trường hợp ngoại lệ: Khi tôi cần tạo một đối tượng truyền hoặc đối tượng cấu hình, tôi sẽ sử dụng mẫu xây dựng để tạo các đối tượng được xây dựng nhỏ trước khi xây dựng một đối tượng cấu hình lớn.


16

Một khía cạnh mà các câu trả lời khác không đưa ra là hiệu suất.

Nếu bạn đang lập trình bằng ngôn ngữ cấp độ đủ thấp (C, C ++, lắp ráp), một số lượng lớn các tham số có thể gây bất lợi cho hiệu suất trên một số kiến ​​trúc, đặc biệt nếu hàm được gọi là số lượng lớn thời gian.

Khi có cuộc gọi chức năng được thực hiện trong ARM ví dụ, bốn đối số đầu tiên được đặt trong thanh ghi r0tới r3và đối số còn lại phải được đẩy vào stack. Giữ số lượng đối số dưới năm có thể tạo ra sự khác biệt cho các hàm quan trọng.

Đối với các chức năng được gọi là cực kỳ thường xuyên, ngay cả những thực tế mà chương trình đã thiết lập các đối số trước mỗi cuộc gọi có thể ảnh hưởng đến hiệu suất ( r0để r3có thể được ghi đè bởi các chức năng gọi và sẽ phải được thay thế trước khi cuộc gọi tiếp theo) để trong đó liên quan không đối số là tốt nhất.

Cập nhật:

KjMag đưa ra chủ đề thú vị của nội tuyến. Nội tuyến theo một số cách sẽ giảm thiểu điều này vì nó sẽ cho phép trình biên dịch thực hiện các tối ưu hóa tương tự mà bạn có thể làm nếu viết trong lắp ráp thuần túy. Nói cách khác, trình biên dịch có thể xem các tham số và biến nào được sử dụng bởi hàm được gọi và có thể tối ưu hóa việc sử dụng thanh ghi để ngăn xếp đọc / ghi của ngăn xếp.

Có một số vấn đề với nội tuyến mặc dù.

  1. Nội tuyến làm cho nhị phân được biên dịch phát triển do cùng một mã được sao chép ở dạng nhị phân nếu nó được gọi từ nhiều nơi. Điều này gây bất lợi khi sử dụng I-cache.
  2. Trình biên dịch thường chỉ cho phép nội tuyến đến một mức nhất định (3 bước IIRC?). Hãy tưởng tượng gọi một hàm nội tuyến từ một hàm nội tuyến từ một hàm nội tuyến. Tăng trưởng nhị phân sẽ bùng nổ nếu inlineđược coi là bắt buộc trong mọi trường hợp.
  3. Có rất nhiều trình biên dịch sẽ hoàn toàn bỏ qua inlinehoặc thực sự gây ra lỗi cho bạn khi họ gặp phải nó.

Việc vượt qua một số lượng lớn các tham số là tốt hay xấu từ góc độ hiệu suất phụ thuộc vào các lựa chọn thay thế. Nếu một phương thức cần hàng tá thông tin và người ta sẽ gọi nó hàng trăm lần với cùng một giá trị cho mười một trong số chúng, thì phương thức lấy một mảng có thể nhanh hơn việc lấy hàng tá tham số. Mặt khác, nếu mỗi cuộc gọi sẽ cần một bộ mười hai giá trị duy nhất, việc tạo và điền vào mảng cho mỗi cuộc gọi có thể dễ dàng chậm hơn so với việc chuyển trực tiếp các giá trị.
supercat

Không nội tuyến giải quyết vấn đề này?
KjMag

@KjMag: Vâng, ở một mức độ nhất định. Nhưng có rất nhiều gotchas tùy thuộc vào trình biên dịch. Các hàm thường sẽ chỉ được nội tuyến đến một mức nhất định (nếu bạn gọi một hàm được gọi là hàm được gọi là hàm nội tuyến ....). Nếu hàm lớn và được gọi từ rất nhiều nơi, thì nội tuyến ở khắp mọi nơi làm cho nhị phân lớn hơn có thể có nghĩa là nhiều lỗi hơn trong I-cache. Vì vậy, nội tuyến có thể giúp đỡ, nhưng nó không phải là viên đạn bạc. (Chưa kể có khá nhiều trình biên dịch nhúng cũ không hỗ trợ inline.)
Leo

7

Khi danh sách tham số tăng lên hơn năm, hãy xem xét việc xác định cấu trúc hoặc đối tượng "bối cảnh".

Về cơ bản, đây chỉ là một cấu trúc chứa tất cả các tham số tùy chọn với một số giá trị mặc định hợp lý được đặt.

Trong thế giới thủ tục C, một cấu trúc đơn giản sẽ làm. Trong Java, C ++, một đối tượng đơn giản sẽ đủ. Đừng lộn xộn với getters hoặc setters vì mục đích duy nhất của đối tượng là giữ các giá trị có thể giải quyết "công khai".


Tôi đồng ý, một đối tượng bối cảnh có thể trở nên rất tiện dụng khi các cấu trúc tham số chức năng bắt đầu trở nên khá phức tạp. Gần đây tôi đã viết blog về việc sử dụng các đối tượng bối cảnh với mô hình giống như khách truy cập
Lukas Eder

5

Không, không có hướng dẫn tiêu chuẩn

Nhưng có một số kỹ thuật có thể làm cho một hàm với nhiều tham số dễ chịu hơn.

Bạn có thể sử dụng tham số list-if-args (args *) hoặc tham số dictionary-of-args (kwargs **)

Chẳng hạn, trong python:

// Example definition
def example_function(normalParam, args*, kwargs**):
  for i in args:
    print 'args' + i + ': ' + args[i] 
  for key in kwargs:
    print 'keyword: %s: %s' % (key, kwargs[key])
  somevar = kwargs.get('somevar','found')
  missingvar = kwargs.get('somevar','missing')
  print somevar
  print missingvar

// Example usage

    example_function('normal parameter', 'args1', args2, 
                      somevar='value', missingvar='novalue')

Đầu ra:

args1
args2
somevar:value
someothervar:novalue
value
missing

Hoặc bạn có thể sử dụng cú pháp định nghĩa bằng chữ

Ví dụ: đây là lệnh gọi JavaScript jQuery để khởi chạy yêu cầu AJAX GET:

$.ajax({
  type: 'GET',
  url: 'http://someurl.com/feed',
  data: data,
  success: success(),
  error: error(),
  complete: complete(),
  dataType: 'jsonp'
});

Nếu bạn xem lớp ajax của jQuery, có rất nhiều (khoảng 30) thuộc tính khác có thể được đặt; chủ yếu là vì truyền thông ajax rất phức tạp. May mắn thay, cú pháp đối tượng làm cho cuộc sống dễ dàng.


C # intellisense cung cấp tài liệu tham số tích cực cho các tham số để không thấy các sắp xếp quá phức tạp của các phương thức quá tải.

Các ngôn ngữ được gõ động như python / javascript không có khả năng như vậy, do đó, rất phổ biến để xem các đối số từ khóa và định nghĩa nghĩa đen của đối tượng.

Tôi thích các định nghĩa bằng chữ của đối tượng ( ngay cả trong C # ) để quản lý các phương thức phức tạp vì bạn có thể thấy rõ các thuộc tính nào đang được đặt khi một đối tượng được khởi tạo. Bạn sẽ phải làm thêm một chút công việc để xử lý các đối số mặc định nhưng về lâu dài mã của bạn sẽ dễ đọc hơn rất nhiều. Với các định nghĩa theo nghĩa đen của đối tượng, bạn có thể phá vỡ sự phụ thuộc vào tài liệu để hiểu mã của bạn đang làm gì ngay từ cái nhìn đầu tiên.

IMHO, phương pháp quá tải được đánh giá cao.

Lưu ý: Nếu tôi nhớ điều khiển truy cập chỉ đọc đúng sẽ hoạt động cho các hàm tạo đối tượng theo nghĩa đen trong C #. Về cơ bản chúng hoạt động giống như cài đặt các thuộc tính trong hàm tạo.


Nếu bạn chưa bao giờ viết bất kỳ mã không tầm thường nào bằng ngôn ngữ dựa trên javaScript được gõ động (python) và / hoặc chức năng / nguyên mẫu, tôi khuyên bạn nên dùng thử. Nó có thể là một kinh nghiệm khai sáng.

Điều đáng sợ trước tiên là phá vỡ sự phụ thuộc của bạn vào các tham số cho cách tiếp cận cuối cùng, tất cả để khởi tạo chức năng / phương thức nhưng bạn sẽ học được nhiều hơn nữa với mã của mình mà không phải thêm độ phức tạp không cần thiết.

Cập nhật:

Tôi có lẽ nên cung cấp các ví dụ để chứng minh việc sử dụng ngôn ngữ được nhập tĩnh nhưng hiện tại tôi không suy nghĩ trong ngữ cảnh được nhập tĩnh. Về cơ bản, tôi đã làm quá nhiều việc trong bối cảnh được gõ động để đột nhiên chuyển trở lại.

Những gì tôi làm biết là đối tượng cú pháp định nghĩa đen là hoàn toàn có thể bằng các ngôn ngữ tĩnh đánh máy (ít nhất là trong C # và Java) bởi vì tôi đã sử dụng chúng trước. Trong các ngôn ngữ được nhập tĩnh, chúng được gọi là 'Đối tượng khởi tạo'. Dưới đây là một số liên kết để hiển thị việc sử dụng chúng trong JavaC # .


3
Tôi không chắc chắn tôi thích phương pháp này, chủ yếu là do bạn mất giá trị tự ghi lại các tham số riêng lẻ. Đối với danh sách các mục tương tự, điều này có ý nghĩa hoàn hảo (ví dụ: một phương thức lấy danh sách các chuỗi và nối chúng) nhưng đối với một tham số tùy ý, điều này tệ hơn so với lệnh gọi phương thức dài.
Michael K

@MichaelK Hãy xem cái khác về khởi tạo đối tượng. Chúng cho phép bạn xác định rõ ràng các thuộc tính trái ngược với cách chúng được định nghĩa ngầm trong các tham số phương thức / hàm truyền thống. Đọc cái này, msdn.microsoft.com/en-us/l Library / bb397680.aspx , để xem ý tôi là gì.
Evan Plaice

3
Tạo một loại mới chỉ để xử lý danh sách tham số nghe có vẻ chính xác như định nghĩa về độ phức tạp không cần thiết ... Chắc chắn, các ngôn ngữ động cho phép bạn tránh điều đó nhưng sau đó bạn nhận được một bóng tham số goo. Bất kể, điều này không trả lời câu hỏi.
Telastyn

@Telastyn Bạn đang nói về cái gì vậy? Không có kiểu mới nào được tạo, bạn khai báo các thuộc tính trực tiếp bằng cú pháp đối tượng. Nó giống như định nghĩa một đối tượng ẩn danh nhưng phương thức diễn giải nó như là một nhóm tham số key = value. Những gì bạn đang xem là một khởi tạo phương thức (không phải là một đối tượng đóng gói tham số). Nếu thịt bò của bạn có bao bì tham số, hãy xem mẫu Đối tượng tham số được đề cập trong một trong những câu hỏi khác vì đó chính xác là nó.
Evan Plaice

@EvanPlaice - Ngoại trừ các ngôn ngữ lập trình tĩnh lớn và yêu cầu loại khai báo (thường là mới) để cho phép mẫu Đối tượng tham số.
Telastyn

3

Cá nhân, hơn 2 là nơi mã của tôi kích hoạt cảnh báo mùi. Khi bạn coi các hàm là các hoạt động (nghĩa là một bản dịch từ đầu vào sang đầu ra), sẽ không phổ biến khi có hơn 2 tham số được sử dụng trong một hoạt động. Các thủ tục (đó là một loạt các bước để đạt được mục tiêu) sẽ cần nhiều đầu vào hơn và đôi khi là cách tiếp cận tốt nhất, nhưng trong hầu hết các ngôn ngữ ngày nay không nên là chuẩn mực.

Nhưng một lần nữa, đó là hướng dẫn chứ không phải là một quy tắc. Tôi thường có các hàm mất nhiều hơn hai tham số do hoàn cảnh bất thường hoặc dễ sử dụng.


2

Rất giống như Evan Plaice đang nói, tôi là một fan hâm mộ lớn của việc đơn giản chuyển các mảng kết hợp (hoặc cấu trúc dữ liệu có thể so sánh của ngôn ngữ của bạn) vào các chức năng bất cứ khi nào có thể.

Do đó, thay vì (ví dụ) này:

<?php

createBlogPost('the title', 'the summary', 'the author', 'the date of publication, 'the keywords', 'the category', 'etc');

?>

Đi:

<?php

// create a hash of post data
$post_data = array(
  'title'    => 'the title',
  'summary'  => 'the summary',
  'author'   => 'the author',
  'pubdate'  => 'the publication date',
  'keywords' => 'the keywords',
  'category' => 'the category',
  'etc'      => 'etc',
);

// and pass it to the appropriate function
createBlogPost($post_data);

?>

Wordpress thực hiện rất nhiều thứ theo cách này và tôi nghĩ nó hoạt động tốt. (Mặc dù mã ví dụ của tôi ở trên là tưởng tượng và bản thân nó không phải là ví dụ từ Wordpress.)

Kỹ thuật này cho phép bạn truyền rất nhiều dữ liệu vào các chức năng của mình một cách dễ dàng, nhưng giải phóng bạn khỏi việc phải nhớ thứ tự mà mỗi dữ liệu phải được thông qua.

Bạn cũng sẽ đánh giá cao kỹ thuật này khi đến lúc tái cấu trúc - thay vì có khả năng thay đổi thứ tự các đối số của hàm (chẳng hạn như khi bạn nhận ra rằng bạn cần phải vượt qua đối số Yet Another), bạn không cần thay đổi tham số của hàm danh sách nào cả.

Điều này không chỉ giúp bạn không phải viết lại định nghĩa hàm - nó còn giúp bạn không phải thay đổi thứ tự các đối số mỗi khi hàm được gọi. Đó là một chiến thắng lớn.


Việc xem bài đăng này nêu bật một lợi ích khác của cách tiếp cận pass-a-hash: lưu ý rằng ví dụ mã đầu tiên của tôi quá dài, nó tạo ra một thanh cuộn, trong khi cái thứ hai vừa vặn trên trang. Điều tương tự có thể sẽ đúng trong trình soạn thảo mã của bạn.
Chris Allen Lane

0

Một câu trả lời trước đã đề cập đến một tác giả đáng tin cậy đã nói rằng các chức năng của bạn càng ít tham số thì bạn càng làm tốt hơn. Câu trả lời không giải thích tại sao nhưng các cuốn sách giải thích nó, và đây là hai trong số những lý do thuyết phục nhất là tại sao bạn cần chấp nhận triết lý này và cá nhân tôi đồng ý với:

  • Các tham số thuộc về một mức độ trừu tượng khác với mức độ của hàm. Điều này có nghĩa là người đọc mã của bạn sẽ phải suy nghĩ về bản chất và mục đích của các tham số của các chức năng của bạn: suy nghĩ này là "mức thấp hơn" so với tên và mục đích của các chức năng tương ứng của chúng.

  • Lý do thứ hai để có càng ít tham số càng tốt cho một hàm là kiểm tra: ví dụ: nếu bạn có một hàm có 10 tham số, hãy nghĩ xem bạn có bao nhiêu kết hợp tham số để bao gồm tất cả các trường hợp kiểm tra, ví dụ, một đơn vị kiểm tra. Ít tham số = ít kiểm tra.


0

Để cung cấp thêm một số bối cảnh xung quanh lời khuyên cho số lượng đối số hàm lý tưởng là 0 trong "Mã sạch: Sổ tay thủ công phần mềm linh hoạt" của Robert Martin, tác giả cho biết như sau đây là một trong những điểm của mình:

Luận cứ thì khó. Họ có rất nhiều sức mạnh khái niệm. Đó là lý do tại sao tôi loại bỏ gần như tất cả chúng khỏi ví dụ. Hãy xem xét, ví dụ, StringBuffertrong ví dụ. Chúng ta có thể đã vượt qua nó như một đối số thay vì biến nó thành một biến đối tượng, nhưng sau đó độc giả của chúng ta sẽ phải giải thích nó mỗi khi họ nhìn thấy nó. Khi bạn đang đọc câu chuyện được kể bởi mô-đun, includeSetupPage() sẽ dễ hiểu hơn includeSetupPageInto(newPageContent). Đối số ở một mức độ trừu tượng khác nhau mà tên hàm và buộc bạn phải biết một chi tiết (nói cách khác, StringBuffer) không đặc biệt quan trọng tại thời điểm đó.

includeSetupPage()dụ của anh ấy ở trên, đây là một đoạn nhỏ về "mã sạch" được tái cấu trúc của anh ấy ở cuối chương:

// *** NOTE: Commments are mine, not the author's ***
//
// Java example
public class SetupTeardownIncluder {
    private StringBuffer newPageContent;

    // [...] (skipped over 4 other instance variables and many very small functions)

    // this is the zero-argument function in the example,
    // which calls a method that eventually uses the StringBuffer instance variable
    private void includeSetupPage() throws Exception {
        include("SetUp", "-setup");
    }

    private void include(String pageName, String arg) throws Exception {
        WikiPage inheritedPage = findInheritedPage(pageName);
        if (inheritedPage != null) {
            String pagePathName = getPathNameForPage(inheritedPage);
            buildIncludeDirective(pagePathName, arg);
        }
    }

    private void buildIncludeDirective(String pagePathName, String arg) {
        newPageContent
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n");
    }
}

"Trường phái tư tưởng" của tác giả lập luận cho các lớp nhỏ, số lượng đối số hàm thấp (lý tưởng là 0) và các hàm rất nhỏ. Mặc dù tôi cũng không hoàn toàn đồng ý với anh ta, tôi thấy nó kích thích tư duy và tôi cảm thấy rằng ý tưởng về các đối số hàm số 0 là một lý tưởng có thể đáng xem xét. Ngoài ra, lưu ý rằng ngay cả đoạn mã nhỏ của anh ta ở trên cũng có các hàm đối số khác không, vì vậy tôi nghĩ rằng nó phụ thuộc vào ngữ cảnh.

.


-2

Lý tưởng là không. Một hoặc hai là ok, ba trong một số trường hợp nhất định.
Bốn hoặc nhiều hơn thường là một thực hành xấu.

Cũng như các nguyên tắc trách nhiệm duy nhất mà người khác đã lưu ý, bạn cũng có thể nghĩ về nó từ các quan điểm thử nghiệm và gỡ lỗi.

Nếu có một tham số, biết giá trị của nó, kiểm tra chúng và tìm lỗi với chúng là 'tương đối dễ vì chỉ có một yếu tố. Khi bạn tăng các yếu tố, tổng độ phức tạp tăng nhanh. Đối với một ví dụ trừu tượng:

Hãy xem xét một chương trình 'mặc gì trong thời tiết này'. Xem xét những gì nó có thể làm với một đầu vào - nhiệt độ. Như bạn có thể tưởng tượng, kết quả của việc mặc gì khá đơn giản dựa trên một yếu tố đó. Bây giờ hãy xem xét những gì chương trình có thể / có thể / nên làm nếu nó thực sự vượt qua nhiệt độ, độ ẩm, điểm sương, lượng mưa, v.v ... Bây giờ hãy tưởng tượng nó sẽ khó gỡ lỗi như thế nào nếu đưa ra câu trả lời 'sai' cho điều gì đó.


12
Nếu một hàm có các tham số bằng 0, thì nó sẽ trả về một giá trị không đổi (hữu ích trong một số trường hợp, nhưng hạn chế hơn) hoặc nó đang sử dụng một số trạng thái được che giấu sẽ tốt hơn nên được làm rõ ràng. (Trong các cuộc gọi phương thức OO, đối tượng ngữ cảnh đủ rõ ràng rằng nó không gây ra sự cố.)
Donal Fellows

4
-1 vì không trích dẫn nguồn
Joshua Drake

Bạn có nghiêm túc nói rằng lý tưởng là tất cả các chức năng sẽ không có tham số? Hay là cường điệu này?
GreenAsJade

1
Xem đối số của chú Bob tại: notifyit.com/articles/article.aspx?p=1375308 và lưu ý rằng ở phía dưới, ông nói "Các hàm nên có một số lượng nhỏ các đối số. Không có đối số nào là tốt nhất, theo sau là một, hai và ba Hơn ba là rất đáng nghi ngờ và nên tránh với định kiến. "
Michael Durrant

Tôi đã đưa ra nguồn. Hài hước không có ý kiến ​​kể từ đó. Tôi cũng đã cố gắng trả lời phần 'hướng dẫn' vì nhiều người hiện coi chú Bob và Clean Code là hướng dẫn. Điều thú vị là câu trả lời cực kỳ hấp dẫn (hiện tại) nói rằng không biết bất kỳ hướng dẫn nào. Chú Bob không có ý định có thẩm quyền nhưng phần nào đó là như vậy và câu trả lời này ít nhất là cố gắng trở thành một câu trả lời cho các chi tiết cụ thể của câu hỏi.
Michael Durrant
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.