Скільки параметрів занадто багато? [зачинено]


228

Рутини можуть мати параметри, це не новина. Ви можете визначити стільки параметрів, скільки вам може знадобитися, але занадто багато їх буде ускладнювати розуміння та підтримку розпорядок дня.

Звичайно, ви можете використовувати структуровану змінну як спосіб вирішення: розміщення всіх цих змінних в одній структурі та передача їх у порядок. Насправді, використання структур для спрощення списків параметрів - один із методів, описаних Стівом МакКоннеллом у Code Complete . Але як він каже:

Дбайливі програмісти уникають об'єднання даних більше, ніж це логічно необхідно.

Тож якщо у вашій рутині занадто багато параметрів або ви використовуєте структуру для маскування великого списку параметрів, ви, ймовірно, робите щось не так. Тобто ви не тримаєте муфту вільно.

Моє запитання, коли я можу вважати список параметрів занадто великим? Я думаю, що понад 5 параметрів, занадто багато. Що ти думаєш?


1
Просто згадуючи тут це питання, який є практичним прикладом того, скільки параметрів занадто багато ...
Гедеон

5
У JavaScript 65537 параметрів занадто багато: jsfiddle.net/vhmgLkdm
Aadit M Shah

просто подивіться на компоненти компонентів apache http, що майже неможливо побудувати з цього щось динамічне
Ендрю Скотт Еванс

Відповіді:


162

Коли щось вважається настільки нецензурним, що може бути регламентовано, незважаючи на те, що перша поправка гарантує свободу слова? За словами правосуддя Поттера Стюарта, "я знаю це, коли бачу". Те ж саме і тут.

Я ненавиджу такі жорсткі та швидкі правила, як це, тому що відповідь змінюється не лише залежно від розміру та обсягу вашого проекту, але я думаю, що він змінюється навіть до рівня модуля. Залежно від того, чим займається ваш метод, або що повинен представляти клас, цілком можливо, що 2 аргументи занадто багато і є симптомом занадто сильної зв'язку.

Я б запропонував, поставивши запитання в першу чергу і кваліфікувавши своє запитання так само, як і ви, що ви дійсно все це знаєте. Найкращим рішенням тут є не покладатися на жорсткий та швидкий номер, а замість того, щоб подивитися на огляди дизайну та огляди коду серед ваших однолітків, щоб визначити сфери, де у вас низька згуртованість та тісна зв’язок.

Ніколи не бійтеся показувати колегам свою роботу. Якщо ви боїтесь, це, мабуть, більший знак того, що з вашим кодом щось не так, і що ви вже це знаєте .


Хорошим правилом є кількість реєстрів процесорів, тому що більше, і компілятор буде змушений виділяти їх на стек.
Michaelangel007

1
@ Michaelangel007 щодо відповіді, яку ви коментуєте, ви не повинні цього говорити, оскільки вона говорить про те, що немає правила. Крім того, кількість параметрів - це питання читабельності, а не продуктивності.
clemp6r

1
@ clemp6r Неправильно - це і те, і інше . Хлопцям-компіляторам доводиться весь час мати справу з "реєстрацією розливу". Тільки авторитетну відповідь , щоб перевірити зборку , що генерує компілятор. Кількість регістрів не змінюється магічно на одній платформі. en.wikipedia.org/wiki/Register_allocation
Michaelangel007

124

Функція може мати занадто багато параметрів, лише якщо деякі параметри є зайвими. Якщо використовуються всі параметри, функція повинна мати правильну кількість параметрів. Візьміть цю часто використовувану функцію:

HWND CreateWindowEx
(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

Це 12 параметрів (9, якщо ви поєднаєте x, y, w і h у вигляді прямокутника), а також є параметри, похідні від назви класу. Як би ви зменшили це? Чи хотіли б ви зменшити кількість до кращого?

Не дозволяйте, щоб кількість параметрів вас турбувало, просто переконайтесь, що це логічно і добре зафіксовано, і нехай intellisense * допоможе вам.

* Доступні інші помічники кодування!


37
Я це голосую. Я вражений усіма іншими відповідями: "3" та "4"! Правильна відповідь: необхідний мінімум, якого іноді може бути досить багато.
Тоні Ендрюс

16
Якби ця функція була розроблена сьогодні, вона, мабуть, виглядала б трохи інакше. x, y, nWidth та nHeight можна об'єднати в об'єкт Прямокутник. стиль і xStyle можна поєднувати в набір переліків або рядків. Тепер у вас всього 8 параметрів.
finnw

16
@finnw Якби ця функція була розроблена сьогодні? Він уже перероблений. Форма f = нова форма (); має 0 параметрів.
Нік

36
Це С і вінапі. Я не можу придумати гіршого прикладу.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

17
Ні ні. Це процедурний стиль. Windows - це об'єкти, тому це зараз застаріло. Сьогодні це вирішиться за допомогою агрегованих класів, а не з купою параметрів. Інтуїтивне правило гарного дизайну - якщо ви не можете описати функцію (включаючи параметри) одним простим реченням, вона погано розроблена . Я вважаю, що це так.
Jan Turoň

106

У « Чистому кодексі» Роберт К. Мартін присвятив цій темі чотири сторінки. Ось суть:

Ідеальна кількість аргументів для функції дорівнює нулю (niladic). Далі йде одна (монадійна), а за нею близько - двоє (діадична). По можливості слід уникати трьох аргументів (тріадичних). Більше трьох (поліадичних) вимагає дуже спеціального обґрунтування - і тоді їх не слід використовувати.


2
Я сумніваюся, що вона включає "це", тому що це контекст страти. У функціональному програмуванні контекст є глобальним і в oop, контекст - це об'єкт, щодо якого ви застосовуєте метод. Крім того, якби ви включили "це" у список параметрів, не можна мати 0 парам (ідеально).
Том

1
Ні, це не включає "це".
Патрік МакЕлхані

20
Крім того, Мартін повинен поговорити зі стандартним комітетом C ++. Половина <алгоритму> приймає більше 3 параметрів, тому що лише один діапазон ітераторів вже 2. Це просто природа програмування з ітераторами (тобто загальне програмування з колекціями STL).
Стів Джессоп

3
@SteveJessop: помилка <алгоритму> в тому, що він завжди працює на фрагменті. Якби алгоритм і колекція перероблялися, я б змушував алгоритми завжди брати цілу колекцію, і ви отримаєте спосіб розрізати подання на колекцію, щоб алгоритми могли працювати на частини; Крім того, я б також визначив скорочення скорочень, щоб полегшити роботу над загальною справою.
Лі Лі Раян

3
@LieRyan: тоді як, якщо я перепроектував, <algorithm>я б змусив його діяти на об'єкт діапазону. Колекції були б діапазонами, але не всі діапазони були колекціями. І справді, стимул вже зробив це. У будь-якому разі, моя думка полягає в тому, що бібліотека, що широко використовується, ігнорує цю пораду, тому найгірше, що гарантовано трапиться з вами, якщо ви теж зробите це, - це те, що багато ваших мільйонів користувачів поворухнуться з незначними спрощеннями вашого інтерфейсу ;-)
Стів Jessop

79

Якийсь код, з яким я працював у минулому, використовував глобальні змінні, щоб уникнути занадто великої кількості параметрів.

Будь ласка, не робіть цього!

(Зазвичай.)


Якийсь код я працював над використовуваними членами класу, щоб досягти того самого. У той час я був програмістом на C і я запитав: чому так багато глобальних змінних, чому введення / виходи не можуть бути явно параметрами?
користувач3528438

38

Якщо вам потрібно подумки відраховувати параметри в підписі і співставляти їх з викликом, тоді настає час для рефактора!


Це дуже гарна відповідь. Якщо параметри організовані логічно (x, y, w, h), їх легко запам'ятати в правильному порядку. Мудді складніше перезаписатись, куди поставити вказівник FILE в putc (який має лише два параметри), тим більше, що fprintf є протилежним.
user877329

Найприємніша відповідь. Дуже добре сказано
Анвар

31

Дуже дякую за всі ваші відповіді:

  • Було трохи дивно, коли я знайшов людей, які також думають (як і я), що 5 параметрів є хорошим обмеженням для надійності коду.

  • Як правило, люди сходяться на думці, що межа між 3 і 4 - це добре правило. Це розумно, оскільки люди зазвичай погано перераховують більше 4 речей.

  • У Мілані точок , в середньому людина може тримати більш-менш 7 речей в їх голові в той час. Але я думаю, що ви не можете забути, що, розробляючи / підтримуючи / вивчаючи рутину, потрібно пам’ятати більше про речі, а не лише про параметри.

  • Деякі люди вважають, що рутина повинна мати стільки аргументів, скільки потрібно. Я погоджуюся, але лише для кількох конкретних випадків (дзвінки в API API, підпрограми, де важлива оптимізація тощо). Я пропоную приховати складність цих процедур, додавши шар абстракції трохи вище цих дзвінків, коли це можливо.

  • Нік має цікаві думки з цього приводу. Якщо ви не хочете читати його коментарі, підсумовую: у двох словах, це залежить :

    Я ненавиджу такі жорсткі та швидкі правила, як це, тому що відповідь змінюється не лише залежно від розміру та обсягу вашого проекту, але я думаю, що він змінюється навіть до рівня модуля. Залежно від того, чим займається ваш метод, або що повинен представляти клас, цілком можливо, що 2 аргументи занадто багато і є симптомом занадто сильної зв'язку.

    Мораль тут не бійтеся показувати свій код одноліткам, обговоріть з ними і намагайтеся "визначити сфери, де у вас низька згуртованість і тісний зв'язок" .

  • Нарешті, я думаю, що Wnoise дуже погоджується з Ніком, і завершує свій сатиричний внесок цим поетичним баченням (див. Коментарі нижче) мистецтва програмування:

    Програмування - це не інженерія. Організація коду - це мистецтво, оскільки воно залежить від людських факторів, які занадто сильно залежать від контексту для будь-якого жорсткого правила.


16

Ця відповідь передбачає мову ОО. Якщо ви не використовуєте - пропустіть цю відповідь (це не зовсім мовно-агностична відповідь іншими словами.

Якщо ви передаєте більше 3-х параметрів (особливо внутрішніх типів / об'єктів), це не так, що це "Занадто багато", але, можливо, вам не вистачає шансу створити новий об'єкт.

Шукайте групи параметрів, які переходять у більш ніж один метод - навіть група, що передається двома методами, майже гарантує, що у вас має бути новий об’єкт.

Тоді ви перетворюєте функціональність рефактора у свій новий об’єкт, і ви не повірите, наскільки це допомагає як вашому коду, так і вашому розумінню програмування OO.


Будь ласка, назвіть мову, яка не використовує підпрограми та параметри. Я не можу придумати жодного, навіть у зборах можна вважати підпрограми (етикетки).
Аврон

x86 Assembly безумовно має підпрограми (дзвінок / повернення). Іншим може знадобитися комбінація cmp / jmp.
Брайан Ноблеуч

Вибачте, "ret", а не "return". Зітхає. Це вже довгий день.
Брайан Ноблеуч

Вибачте за неоднозначне фразування Аврона, я вважаю, що я це виправив. Це моя відповідь була специфічною для мови, а не питанням.
Білл К

1
Не всі мови дозволяють проходити структури. Крім того, передача структури означає, що метод прийому повинен мати код для обробки структури і, отже, може знадобитися більше параметрів. Крім того, для мови, яка не є oo, вам зазвичай потрібен додатковий параметр - всі мови OO мають параметр "прихований" параметр "this", який інакше слід передати (або, більш жахливою мовою / дизайном, може бути глобально) доступно)
Білл К

13

Схоже, є й інші міркування, ніж просто число, ось деякі, які приходять на думку:

  1. логічне відношення до основного призначення функції проти одноразових налаштувань

  2. Якщо вони є лише прапорами навколишнього середовища, їх комплектація може бути дуже зручною


12

Одна з відомих програмових епіграм Алана Перліса (переказана в повідомленнях ACM SIGPLAN 17 (9), вересень 1982 р.) Говорить, що "Якщо у вас є процедура з 10 параметрами, ви, ймовірно, деякі пропустили".


11

За словами Стіва МакКоннела в " Кодексі повним" , ви повинні

Обмежте кількість параметрів програми приблизно на сім


3
/: Номер, що вказує на те, що складається фактоїд
user877329

9

Для мене, коли список перетинає один рядок у моєму IDE, то це один параметр занадто багато. Я хочу бачити всі параметри в одному рядку, не порушуючи контакт очей. Але це лише мої особисті переваги.


Це добре, поки ви з деякими розробниками не спробуєте foo (int a, float b, string c, double d). Найкраще спробувати уникати роботи з ними. : D
Ронтолог

4
Якщо хтось дав заняттям смішно довгі імена, я б не дозволив це вплинути на те, як я визначаю або називаю свої процедури.
finnw

9
Чи можу я ознайомити вас із "поверненням вагона"?

3
Коли список перетинає один рядок у вашому IDE, ваш монітор занадто малий, щоб використовувати його з цим кодом, і вам слід чітко придбати монітор з більш високою горизонтальною роздільною здатністю. Розрив рядка або зменшення числа параметрів - це лише обхідні шляхи, які не вирішують кореневої проблеми, а саме те, що ваш монітор занадто малий для цієї кодової бази!
Кайзерлуді

9

Я, як правило, погоджуюся з 5, однак, якщо є ситуація, коли мені потрібно більше, і це найясніший спосіб вирішити проблему, то я б скористався більше.


8

Сім речей у короткотерміновій пам'яті?

  1. Назва функції
  2. Повернене значення функції
  3. Призначення функції
  4. Параметр 1
  5. Параметр 2
  6. Параметр 3
  7. Параметр 4

Ну. Це правило. Коли ви кодуєте тіло функції, вам не байдуже її ім’я, а повернене значення та його цінність тісно пов'язані.
Аврон

7
8. порядок параметрів
Єва

7

У найгірших 5 фрагментах коду перевірте другий: "Це конструктор". Він має приблизно 37 ⋅ 4 ≈ 150 параметрів:

Ось програміст написав цей конструктор [... S] Оме з вас може подумати, що це великий конструктор, але він використовував автоматичні інструменти генерації коду затемнення [.] NOO, у цьому конструкторі була крихітна помилка, яку я виявив, яка зробила мене зробіть висновок, що цей конструктор був написаний від руки. (до речі, це лише верхня частина конструктора, її не закінчена).

конструктор з понад 150 параметрами


Це так сумно ... Не знаючи про "записи" або "структури" або "об'єкти цінності", які поєднують кілька значень разом і дають їм загальну назву, щоб ви могли ієрархічно представляти багато з них, читабельним чином - це як ходити з взуттям без шнурівки роками, тому що ніхто ніколи не говорив вам, що ви навіть можете це зробити 🙈
yeoman

6

Ще один, ніж потрібно. Я не маю на увазі бути глібом, але є деякі функції, для яких обов'язково потрібно досить багато варіантів. Наприклад:

void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);

Є 6 аргументів, і кожен з них є важливим. Крім того, між ними немає спільного зв’язку, який би виправдовував їх об'єднання. Можливо, ви могли б визначити "struct mmapargs", але це було б гірше.


Ну, protі flagsможна було б згорнутись, якби дизайнер відчув, що 5 - це якось чарівне число, яке набагато краще, ніж 6. Трохи схожий на спосіб openпоєднання режиму читання / запису з усіма іншими прапорами. І, можливо, ви могли б позбутися offset, вказавши, що відображений розділ починається з поточної позиції пошуку filedes. Я не знаю, чи є ситуації, коли ти можеш досягти mmapрегіону, до якого ти не здатний lseek, але якщо ні, то це не обов'язково.
Стів Джессоп

... тому я думаю, що mmapце є хорошою ілюстрацією того факту, що деякі дизайнери та користувачі віддають перевагу великий довгий список параметрів, тоді як інші вважають за краще пройти кілька кроків, підготувавши меншу кількість параметрів перед тим, як здійснити виклик.
Стів Джессоп

1
@Steve: Заздалегідь встановлення позиції пошуку за допомогою окремого дзвінка запровадило б уможливу гонку. Для деяких API (OpenGL) існує стільки параметрів, які впливають на виклик, що вам дійсно доведеться використовувати стан, але зазвичай кожен виклик повинен бути максимально окремим. Дія на відстані - це шлях у темну сторону.
Бен Войгт

5

Згідно з найкращими практиками Perl , 3 добре, 4 - це занадто багато. Це просто настанова, але в нашому магазині саме цього ми намагаємося дотримуватися.


5

Я сам намалював би обмеження для публічних функцій на 5 параметрів.

IMHO, довгі списки параметрів є прийнятними лише для приватних / локальних допоміжних функцій, які призначені лише для виклику з кількох конкретних місць у коді. У цих випадках вам може знадобитися передавати багато інформації про стан, але читабельність не викликає особливих проблем, оскільки тільки ви (або хтось, хто буде підтримувати ваш код і повинен розуміти основи вашого модуля) повинні дбати про виклик цієї функції.


Саме тому багато людей в цей момент зробили б все, що не є фактичним аргументом, а перенесеним станом, просто станом об'єкта у вигляді полів (змінних членів). Цей об'єкт буде представлений як клас. Це було б створено миттєво або один раз, або для кожного використання. Іноді такий об’єкт матиме лише один приватний або публічний метод пакету, який потім делегує роботу до приватних методів з кількома аргументами кожен. Зараз мало можливих аргументів, оскільки конфігурація та стан тепер мають належне місце :)
yeoman

5

Пов'язане питання, яке ви повинні розглянути, - наскільки згуртований режим. Велика кількість параметрів може бути запахом, який говорить вам про те, що сама рутина намагається зробити занадто багато, і, отже, є згуртованість підозрілою. Я погоджуюсь, що важке і швидке число параметрів, ймовірно, неможливо, але я б здогадався, що високий рівень згуртованості означатиме малу кількість параметрів.



4

Я зупиняюся на трьох параметрах як загальне правило. Більше, і настав час передати замість цього масив параметрів або об’єкт конфігурації, що також дозволяє додавати майбутні параметри без зміни API.


Якщо API змінюється, то API насправді повинен змінюватись, а не просто змінювати приховані зміни, де несумісність все-таки може статися, але бути менш очевидними.
wnoise

однак, якщо вам потрібен ще один параметр для налаштування крайового регістру, він не повинен поширюватися на неспоріднені компоненти за допомогою API
Eran Galperin

4

Обмеження довжини у списку параметрів - це лише одне обмеження. А обмеження означає застосування насильства. Це звучить смішно, але ви можете бути ненасильницьким навіть при програмуванні. Просто нехай код диктує правила. Очевидно, що якщо у вас багато параметрів, тіло методу функції / класу буде досить великим, щоб ними скористатися. А фрагменти великого коду, як правило, можна відремонтувати і розділити на менші шматки. Таким чином, ви отримуєте рішення проти безлічі параметрів як безкоштовного бонусу, оскільки вони розділені між меншими відреставрованими фрагментами коду.


4

Одне, що я зазначив би з точки зору продуктивності, - це те, що залежно від того, як ви передаєте параметри методу, передача безлічі параметрів за значенням сповільнить програму, оскільки кожен параметр повинен бути скопійований і розміщений у стеці.

Використання одного класу для охоплення всіх параметрів працювало б краще, оскільки один параметр, переданий посиланням, був би елегантнішим та чистішим та швидшим!


Я даю цю відповідь +1, оскільки вона єдина, яка обговорює будь-що, крім чистоти коду чи довільних обмежень, які є і суб'єктивними. Деякі люди можуть не замислюватися над тим, що ви робите зі стеком, коли ви знаходитесь у циклі і висуваєте десятки аргументів на стек і вимикаєте його. Якщо він знаходиться в циклі, вам слід подумати про використання кількості аргументів, які передаються REGISTERS в ABI, який ви компілюєте. Наприклад, у MS x64 ABI максимальна кількість аргументів, що передаються через регістри, становить 4. ABI "System V" (використовується ОС, що не є ОС Windows) використовує більше регістрів, тому для використання 4 аргументів досить портативні
Lakey

3

На мою думку, можуть бути випадки, коли ви перевищуватимете 4 чи якусь фіксовану кількість. Що можна оглянути

  1. Ваш метод робить занадто багато, і вам потрібно зробити рефактор.
  2. Ви можете розглянути можливість використання колекції або якоїсь структури даних.
  3. Перегляньте дизайн свого класу, можливо, деякі речі не потрібно переносити навколо.

З точки зору простоти користування чи простоти читання коду, я думаю, що коли вам потрібно «підгорнути» слово підписом методу, це повинно змусити вас зупинитися і подумати, якщо ви не відчуєте себе безпорадним і всі зусилля зробити підпис меншим призведе до ніякого результату. Деякі дуже хороші бібліотеки в минулому та сьогоденні використовують більше 4-5 дитячих колядок.


3

Моє правило полягає в тому, що мені потрібно вміти запам'ятовувати параметри досить довго, щоб подивитися на виклик і сказати, що це робить. Тож якщо я не можу подивитися на метод, а потім перевернутись на виклик методу і запам'ятати, який параметр робить те, що тоді їх занадто багато.

Для мене це дорівнює приблизно 5, але я не такий яскравий. Ваш пробіг може відрізнятися.

Ви можете створити об'єкт із властивостями для утримування параметрів та передачі цього, якщо ви перевищуєте встановлений вами межа. Дивіться книгу " Рефакторинг" Мартіна Фаулера та розділ щодо спрощення викликів методів.


1

Це сильно залежить від середовища, в якому ви працюєте. Візьмемо для прикладу javascript. У javascript найкращим способом передачі параметрів є використання об'єктів з парами ключ / значення, що на практиці означає, що у вас є лише один параметр. В інших системах солодке місце буде в три-чотири.

Зрештою, все зводиться до особистого смаку.


1

Я погоджуся з 3 добре, 4 - це занадто багато, як настанова. Маючи більше 3 параметрів, ви неминуче виконуєте більше одного завдання. Більше одного завдання слід розділити на окремі методи.

Однак, якби я подивився на останній проект, над яким працював, винятків було б багато, і більшість випадків було б важко спуститися до 3 параметрів.


1

Якщо у мене є 7-10 параметрів в одній рутині, я дивлюся на об'єднання їх у новий клас, але не, якщо цей клас був би не що інше, як купа полів з getters та setters - новий клас повинен робити щось інше, ніж переміщення значень у та з. В іншому випадку я б швидше помирився зі списком довгих параметрів.


1
Я з'єднаю його з класом, призначеним лише для даних, якщо він використовується в більш ніж одному місці, але навіть тоді я зазвичай створюю обидва конструктори.
Леян Новаш

1

Відомий факт, що в середньому люди можуть одночасно тримати 7 +/- 2 речі в голові. Мені подобається використовувати цей принцип із параметрами. Якщо припустити, що програмісти - це все розумні люди середнього рівня середнього рівня, я б сказав, що всього 10+ - це занадто багато.

BTW, якщо параметри будь-яким чином схожі, я ставлю їх у вектор або список, а не в структуру чи клас.


1

Я б базував свою відповідь на тому, як часто викликається функція.

Якщо це функція init, яку називають лише один раз, то нехай вона займе 10 пар і більше, кому це важливо.

Якщо це називається купа разів на кадр, то я схильний робити структуру і просто передавати вказівник на неї, оскільки це, як правило, швидше (якщо вважати, що ви не перебудовуєте структуру також кожен раз).


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.