Яка найкраща практика впорядкування параметрів у функції?


52

Іноді (рідко) здається, що найкращим маршрутом є створення функції, яка приймає пристойну кількість параметрів. Однак, коли я це роблю, я відчуваю, що часто вибираю впорядкування параметрів навмання. Зазвичай я йду за "порядком важливості", перш за все найважливішим параметром.

Чи є кращий спосіб зробити це? Чи існує спосіб "найкращої практики" впорядкування параметрів, що підвищує чіткість?


5
Названі параметри роблять це менше, ніж проблемою. Python доводить це до крайності, заглядайте в нього. Хороший приклад - на C # - безліч способів зателефонувати MessageBox.Show. Подивіться і на це.
Робота

8
У Haskell можлива корисність використання часткової функції зазвичай передбачає природний упорядкування аргументів.
tdammers

Що б ви вважали пристойною кількістю параметрів?
Кріс

Я думав> 2. Хоча я думаю, що впорядкування стосується двох параметрів однаково.
Кейсі Паттон

3
@tdammers це також стосується будь-якої мови, яка має пряму підтримку currying
jk.

Відповіді:


55

Загалом: використовуйте .

Напишіть тест на свою функцію, справжній світовий тест.
Щось, що ви насправді хотіли б зробити з цією функцією .

І подивіться, в якому порядку ви їх відклали.

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

наприклад, чи всі вони беруть документ / об'єкт / файл-вказівник / ряд значень / координати як перший аргумент (аргументи)? Заради бога, відповідайте цим аргументам .

Не уникайте плутати своїх колег і майбутнього себе .


12
"Уникайте плутати ... своє майбутнє", загалом, звучить як добра філософія.
Жанна Піндар

28

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

Лінія вибору

  1. Семантика
  2. Важливість / Доречність
  3. Частота використання
  4. Проблеми вводу / виводу

1. Семантика Перша

Особливо в OOP вибирайте параметри, виходячи з їх семантичної значущості для дії чи повідомлення. Підпис добре названого методу з добре названим параметром повинен:

  • відчувати себе природним дзвонити,
  • бути самоописовим з точки зору намірів та поведінки.

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

2. Тоді важливість

Найбільш "значущий" параметр приходить першим (або наступним ...)

3. Потім частота

Частота також має значення, особливо на мові, де ви не назвали параметри, але можуть мати значення за замовчуванням для позиційних параметрів. Це означає, що порядок параметрів не змінюється, і що очевидно, ви не можете встановити параметри N + 1, якщо ви хочете примусити значення за замовчуванням N-го параметра (за винятком випадків, якщо у вашій мові є поняття параметра-власника місця ).

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

4. Не забуваймо введення-виведення

якщо ваш метод / функція займає деякий вхід і видає результат, а останній не повинен бути "повернутий" (через виписку return) або "кинутий" (використовуючи систему виключень), то вам залишається можливість пройти Значення повертається до абонента, використовуючи ваші інші параметри (або вхідний параметр). Це стосується семантики, і в більшості випадків буде доцільно, щоб перші параметри визначали вихід, а останні параметри отримували вихід.

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

(Зауважте, я не згадую глобальних змінних, тому що для чого б ви використовували її, правда?)


Деякі речі, які слід врахувати

  • Корисність

    Більшість з перерахованих вище буде відображатися , природно , якщо ви будете слідувати Zjr в раду : використовувати!

  • Розгляньте рефакторинг

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

  • Розглянемо продуктивність

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

  • Бівена відповідь і згадка про чистому коді рекомендації «s, безумовно , актуальні , як добре!


18

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

У книзі " Чистий код " дядька Боба він переконує, що у методів ніколи не повинно бути більше двох аргументів - і більшість має мати лише один, якщо такий є. У такому разі замовлення є очевидним або неважливим.

Як би недосконало я намагаюся дотримуватися порад дядька Боба - і це покращує код.

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


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

3
кашель PHP кашель . Вибачте - ви щось говорили про те, щоб не турбуватися про порядок параметрів?
Craige

Ви не будете просто мати конструктор для вашого параметра параметра, який бере стільки ж аргументів?
детлі

Ні - тому що об'єкт параметра може бути "складений" над декількома операторами способом, який є більш читабельним.
Беван

2
@Bevan: це дуже дискусійно. Побудова об'єктів таким чином означає, що вони вже не можуть бути незмінні; утримуючи незмінності ваших об'єктів, коли це можливо, але це ще один чудовий спосіб підвищення ремонту.
tdammers

10

Я намагаюся поставити параметри IN на перше, OUT параметри на друге. Існують також деякі природні впорядкування, наприклад createPoint(double x, double y), настійно перевагу createPoint(double y, double x).


5
Іноді краще, навпаки, наприклад, коли завжди є лише один аргумент OUT і змінна кількість аргументів, наприклад, у addAllTo(target, something1, something2).
maaartinus

Хоча я дотримуюся того самого правила, я намагаюся уникати параметрів OUT, коли це можливо, і більшості хороших програмістів. Тож кількість функцій, де ця рекомендація насправді допомагає ОП, повинна бути невеликою.
Doc Brown

7
@DocBrown Ви дійсно не повинні намагатися уникати хороших програмістів, вони можуть бути корисними для того, щоб мати навколо.
RyanfaeScotland

@RyanfaeScotland: чорт, я повинен прочитати свої коментарі двічі, перш ніж публікувати їх (або, щонайменше, протягом п’яти хвилин, я можу їх змінити) ;-)
Doc Brown

6

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

Також, коли є кілька перевантажень методу, я помічаю, що типовим способом є перерахування їх, починаючи з параметрів, загальних для всіх (або більшості) методів, причому кожен різний метод додається до кінця для кожного перевантаження методу, наприклад :

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }


3

Я часто дотримуюсь конвенції C / C ++ про розміщення constспочатку параметрів (тобто параметрів, які ви передаєте за значенням), а потім тих, які ви передаєте за посиланням. Це не обов'язково може бути правильним методом виклику функцій, але, якщо вас цікавить, як кожен компілятор обробляє параметри, ознайомтеся з наступними посиланнями щодо правил, що регулюють та / або порядку, що параметри висуваються на стек.

http://msdn.microsoft.com/en-us/library/zthk2dkh%28v=vs.80%29.aspx

http://en.wikipedia.org/wiki/Calling_convention


Ще одне посилання, яке може вас зацікавити - це msdn.microsoft.com/en-us/library/984x0h58%28v=vs.71%29.aspx Для рекурсивних функцій перегляньте наступне посилання. публікації.gbdirect.co.uk/ c_book/ chapter4/… Не те, що я забув, але, будучи новим користувачем, я не можу опублікувати коментар, який має більше двох посилань .... як засмучує!
Білл

1

Зазвичай я просто йду з упорядкуванням параметрів "що виглядає менш кіптичним". Чим менше разів мені потрібно перейти до визначення методу / функції, тим краще. І приємно, щоб вони назвали параметри, які описують, для чого вони використовуються, таким чином, коли вискакує маленька підказка (VS), це робить це ще простіше.

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


2
"кіптика": чудовий спосіб зробити точку зору.
Беван

2
У вас ніколи не було помилки, @Bevan?

1
У мене є помилки весь час - написання cyprtic було так добре , що я думав , що це було навмисне . Будь ласка, змініть її назад, це допоможе зробити свою думку, навіть якщо це була аварія.
Беван

1
@ Беван, га, добре, я бачу, що ти кажеш. Влучне зауваження! Її змінили назад =)

1

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

Використання декількох параметрів часто є чітким показником того, що ви порушуєте SRP у цьому методі. Метод, який потребує багатьох параметрів, навряд чи зможе зробити лише одне. Вибух може бути математичною функцією або способом конфігурації, де дійсно потрібно кілька параметрів як таких. Я б уникнув декількох параметрів, оскільки диявол уникає святої води. Чим більше параметрів ви використовуєте в методі, тим більше шанс, що метод (занадто) складний; тим більше складність означає: складніше в обслуговуванні і тим менш бажано.

Однак, коли я це роблю, я відчуваю, що часто вибираю впорядкування параметрів навмання. Зазвичай я йду за "порядком важливості", перш за все найважливішим параметром.

У принципі ви обираєте навмання . Звичайно, ви можете подумати, що параметр A більш релевантний, ніж параметр B ; але це може бути не так для користувачів вашого API, які вважають, що B є найбільш релевантним параметром. Тож навіть якщо ви були уважні у виборі замовлення - для інших це може здатися випадковим .

Чи є кращий спосіб зробити це? Чи існує спосіб "найкращої практики" впорядкування параметрів, що підвищує чіткість?

Є кілька способів:

а) Тривіальний випадок: не використовуйте більше одного параметра.

б) Оскільки ви не вказали, яку саме мову обрали, є ймовірність, що ви вибрали мову з названими параметрами . Це приємний синтаксичний цукор, який дозволяє послабити значення впорядкованості параметрів:fn(name:"John Doe", age:36)

Не кожна мова допускає такі смаки. То що ж тоді?

в) Ви можете використати словник / хешмап / асоціативний масив як параметр: наприклад, Javascript дозволить таке: fn({"name":"John Doe", age:36})що недалеко від (b).

г) Звичайно, якщо ви працюєте зі статично набраною мовою, як Java. ви можете використовувати Hashmap , але ви втратите інформацію про інформацію (наприклад, при роботі з HashMap<String, Object>), коли параметри мають різні типи (і їх потрібно робити).

Наступним логічним кроком буде передача Object(якщо ви використовуєте Java) відповідних властивостей або чогось більш легкого, як структура (якщо ви пишете, наприклад, C # або C / C ++).

Практичне правило:

1) Найкращий випадок - вашому методу взагалі не потрібні параметри

2) Хороший випадок - вашому методу потрібен один параметр

3) Терпимий випадок - вашому методу потрібні два параметри

4) Усі інші випадки мають бути відновлені


0

Часто кращий складний об'єкт як параметр - версія бідної людини з названими параметрами, яка працює на більшості платформ. І відкриває двері до параметрів з поведінкою до завантаження.

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


0

Я, як правило, спочатку їх замовляю, ніж за допомогою комбінованої міри важливості та частоти використання відповідно до "почуття" (можна вважати ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency)), а не відповідно до якоїсь конкретної практики.

Однак, як зазначають інші, я думаю, що основна проблема, яка спричиняє цю проблему, полягає у використанні занадто багатьох параметрів (IMHO, нічого> 3 - це занадто багато), і це реальна проблема, з якою ви повинні вирішити. Про це є цікавий пост у блозі Rebecca murphey.

Я думаю, що коли у вас є лише 1-3 аргументи, правильне впорядкування цілком очевидно, і ви просто «відчуваєте», що правильно.


0

Як і у відповіді @Wyatt Barnetts, я рекомендую передати об'єкт замість цього, ніж декілька параметрів або дуже явних параметрів методу. Зазвичай це простіше оновлювати / підтримувати, чіткіше читати та усуває необхідність турбуватися про замовлення . Крім того, занадто багато параметрів методу є кодовим запахом, і є загальні моделі рефакторингу, які ви можете дотримуватися, щоб допомогти виправити його.

Явний приклад:

public int add(int left, int right)
{
  return left + right;
}

Оскільки це досить чітко визначений приклад, а додавання є комутативним (порядок не має значення), тоді просто переходьте з ним.

Однак якщо додати щось складніше:

public SomeComplexReturnValue Calculate(int i, float f, string s, object o)
{
  // do work here
}

Стають:

public class SomeComplexInputForCalculation
{
  public int i; 
  public float f;
  public string s; 
  public object o;
}

public SomeComplexReturnValue Calculate(SomeComplexInputForCalculation input)
{
  // do work here
}

Сподіваюся, це допомагає ...


0

Замовляйте його будь-яким способом, на який, на вашу думку, приносить користь. Наприклад, спочатку функціональні параметри.


0

"Важливий перший" не означає цілу партію. Існують деякі умовності.

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

У списку має бути деяка вільність , тобто ви повинні знати, про що йдеться в аргументі до моменту його читання. Приклад:

CopyFolder (рядок шляху, рекурсивний bool);

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

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

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

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

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