Как лучше всего упорядочить параметры в функции?

52

Иногда (редко) кажется, что создание функции, которая принимает приличное количество параметров, является наилучшим маршрутом. Однако, когда я это делаю, я чувствую, что часто выбираю порядок параметров случайным образом. Я обычно выбираю «по важности», сначала выбираю самый важный параметр.

Есть лучший способ сделать это? Существует ли «наилучшая практика» для упорядочения параметров, которая повышает ясность?

Кейси Паттон
источник
5
Именованные параметры делают это меньше, чем проблема. Python доводит это до крайности, посмотрите на это. Хороший пример в C # - множество способов вызова MessageBox.Show. Посмотрите на это также.
Работа
8
В Haskell возможная полезность применения частичных функций обычно предполагает естественный порядок аргументов.
tdammers
Что бы вы посчитали приличным количеством параметров?
Крис
Я думал> 2. Хотя я предполагаю, что порядок относится к 2 параметрам все же.
Кейси Паттон
3
@tdammers это также верно для любого языка, который имеет прямую поддержку карри
jk.

Ответы:

55

В общем, используйте это .

Напишите тест для вашей функции, тест в реальном мире.
То, что вы на самом деле хотели бы сделать с этой функцией .

И посмотри, в каком порядке ты их положил.

Если у вас уже нет (или вы не знаете) о некоторых функциях, которые выполняют нечто подобное.
В таком случае: соответствуйте тому, что они уже делают, по крайней мере, для первых аргументов.

Например, все ли они принимают документ / объект / указатель файла / ряд значений / координаты в качестве первого аргумента? Ради бога, согласитесь с этими аргументами .

Избегайте путаницы с коллегами и с самим собой .

Zjr
источник
12
«Избегайте путаницы ... ваше будущее я» звучит как хорошая философия в целом.
Жанна Пиндар
28

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

Воронка выбора

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

1. Семантика первая

Особенно в ООП, выбирайте параметры, основываясь на их семантической значимости для действия или сообщения. Подпись хорошо названного метода с хорошо названным параметром должна:

  • чувствовать себя естественно, чтобы позвонить,
  • быть информативным с точки зрения намерений и поведения.

(По этим причинам иногда использование пользовательских типов или псевдонимов вместо примитивов может увеличить выразительность вашей подписи.)

2. Тогда Важность

Самый «значимый» параметр появляется первым (или следующим ...)

3. Тогда частота

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

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

4. Давайте не забудем ввод / вывод

если ваш метод / функция принимает некоторый ввод и производит вывод, и последний не должен быть «возвращен» (через оператор возврата) или «брошен» (используя систему исключений), то у вас остается возможность передать значения обратно к вызывающей стороне, используя другие параметры (или входной параметр). Это относится к семантике, и в большинстве случаев будет иметь смысл, чтобы первые параметры определяли выходные данные, а последние параметры получали выходные данные.

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

(Заметьте, я не упоминаю глобальные переменные, потому что зачем использовать одну, верно?)


Некоторые вещи для рассмотрения

  • Удобство использования

    Большинство из вышеперечисленных проявится естественным образом, если вы последуете совету ZJR : используйте его!

  • Рассмотреть вопрос о рефакторинге

    Если вы беспокоитесь об упорядочении параметров, возможно, эта проблема коренится в вышеприведенном и в том, что ваш API плохо спроектирован. Если у вас слишком много параметров, то, скорее всего, что-то может быть скомпоновано / модульно и реорганизовано .

  • Учитывайте производительность

    Имейте в виду, что реализации некоторых языков будут оказывать очень важное влияние на управление памятью во время выполнения при использовании параметров. Отсюда и причина того, что в книгах по стилю многих языков рекомендуется, чтобы список параметров был простым и коротким . Например, максимум 4 параметра. Я оставляю это как упражнение для вас, чтобы понять, почему.

  • Ответ Бевана и упоминание о рекомендациях Чистого кода , безусловно, также актуальны!

haylem
источник
18

Я с уважением утверждаю, что беспокойство о порядке параметров беспокоит не то, что нужно.

В книге дяди Боба « Чистый код » он убедительно утверждает, что у методов никогда не должно быть более двух аргументов - и у большинства должен быть только один, если он есть. В этом случае упорядочение является очевидным или неважным.

Как бы то ни было, я стараюсь следовать советам дяди Боба - и это улучшает мой код.

В тех редких случаях, когда метод, кажется, требует больше информации, введение объекта параметра является хорошей идеей. Обычно я считаю, что это первый шаг к открытию новой концепции (объекта), которая является ключом к моему алгоритму.

Беван
источник
Я определенно согласен с вами! Мое первое заявление было нацелено на то, чтобы понять, что это не типично для меня, ха-ха. Но в том случае, если я обнаружил, что мне действительно нужно передать некоторые параметры, и не стоит рефакторинга всей программы, я задаюсь вопросом о порядке.
Кейси Паттон
3
кашель PHP кашель . Извините - вы что-то говорили о том, чтобы не беспокоиться о порядке параметров?
Крейг,
Не хотите ли вы просто иметь конструктор для вашего объекта параметра, который принимает столько же аргументов?
детально
Нет - потому что объект параметра может быть «построен» из нескольких операторов таким образом, чтобы он был более читабельным.
Беван
2
@Bevan: это очень спорно. Создание объектов таким способом означает, что они больше не могут быть неизменными; сохранение ваших объектов неизменным, когда это возможно, является еще одним отличным способом повышения удобства обслуживания.
tdammers
10

Я пытаюсь поставить параметры IN первым, а параметры OUT вторым. Есть также некоторые естественные порядок, например createPoint(double x, double y), сильно предпочтительнее createPoint(double y, double x).

quant_dev
источник
5
Иногда лучше наоборот, например, когда всегда есть только один аргумент OUT и переменное число в аргументах, как в addAllTo(target, something1, something2).
Maaartinus
Хотя я следую тому же правилу, я стараюсь по возможности избегать параметров OUT, и большинство хороших программистов тоже. Таким образом, число функций, в которых эта рекомендация действительно помогает ФП, должно быть довольно небольшим.
Док Браун
7
@DocBrown Вы действительно не должны стараться избегать хороших программистов, они могут быть полезны для окружающих.
RyanfaeScotland
@RyanfaeScotland: блин, я должен прочитать мои комментарии дважды, прежде чем публиковать их (или, по крайней мере, в течение пяти минут, которые я могу их изменить) ;-)
Док Браун
6

Я никогда не видел документированной «наилучшей практики» по этой конкретной теме, но мой личный стандарт - перечислять их либо в том порядке, в котором они будут отображаться в методе, для которого они используются, либо, если метод более переход к слою данных. Я перечислю их в том порядке, в котором они будут отображаться в схеме БД или в методах уровня данных.

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

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }
Джоэл Этертон
источник
6

Порядок: вход (ы), выход (ы), необязательные параметры.

Джонатан Ху
источник
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. Для рекурсивных функций просмотрите следующую ссылку. publishing.gbdirect.co.uk/c_book/chapter4/… Не то чтобы я забыл, но, будучи новым пользователем, я не могу опубликовать комментарий, содержащий более двух ссылок .... как расстраивает!
Билл
1

Я обычно просто иду с упорядочением параметров «что выглядит менее кипрски». Чем меньше раз мне нужно перейти к определению метода / функции, тем лучше. И приятно иметь именованные параметры, которые описывают то, для чего они используются, так что, когда всплывает небольшая подсказка (VS), это делает его еще проще.

Если у вас есть строки и строки параметров, вы можете рассмотреть другой дизайн. Отойдите назад и посмотрите, как вы можете разбить это на больше функций / методов. Просто идея, но когда в моей функции есть дюжина параметров, это почти всегда не проблема параметров, а проблема дизайна.


источник
2
"Cyprtic": отличный способ сделать точку.
Беван
2
У тебя никогда не было опечатки, @Bevan?
1
У меня все время есть опечатки - писать Cyprtic было так хорошо, что я думал, что это было специально . Пожалуйста, измените его обратно, это поможет вам понять, даже если это был несчастный случай.
Беван
1
@ Беван, хаха, хорошо, я понимаю, что ты говоришь. Хорошая точка зрения! Его изменили обратно =)
1

Иногда (редко) кажется, что создание функции, которая принимает приличное количество параметров, является наилучшим маршрутом.

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

Однако, когда я это делаю, я чувствую, что часто выбираю порядок параметров случайным образом. Я обычно выбираю «по важности», сначала выбираю самый важный параметр.

В принципе вы выбираете наугад . Конечно, вы можете подумать, что параметр A более важен, чем параметр B ; но это может быть не так для пользователей вашего API, которые считают, что B является наиболее важным параметром. Так что, даже если вы были внимательны при выборе заказа - для других это может показаться случайным .

Есть лучший способ сделать это? Существует ли «наилучшая практика» для упорядочения параметров, которая повышает ясность?

Есть несколько выходов:

а) Тривиальный случай: не используйте более одного параметра.

б) Поскольку вы не указали, какой язык вы выбрали, есть вероятность, что вы выбрали язык с именованными параметрами . Это хороший синтаксический сахар, который позволяет вам ослабить значение упорядочения параметров:fn(name:"John Doe", age:36)

Не каждый язык допускает такие тонкости. И что тогда?

c) Вы могли бы использовать Dictionary / Hashmap / Associative Array в качестве параметра: например, 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 слишком много), и это реальная проблема, которую вы должны решить. Есть интересный пост об этом в блоге Ребекки Мёрфи.

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

shesek
источник
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
}

Надеюсь это поможет...

Longda
источник
0

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

альтернатива
источник
0

«Важно прежде всего» не значит много. Есть несколько условностей.

Если вы передаете вызывающий объект (часто называемый отправителем), он идет первым.

В списке должно быть свободное владение , а это значит, что вы должны знать, о чем идет речь, к тому моменту, когда вы его прочитаете. Пример:

CopyFolder (путь строки, bool рекурсивный);

Если бы вы поставили рекурсив первым, это было бы сбивающим с толку, потому что еще нет контекста. Если вы уже сейчас говорите о копировании (1) папки (2), то рекурсивный аргумент начинает иметь смысл.

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

Тогда вы все равно можете обнаружить, что в этом отношении есть «равные» аргументы, и у вас может возникнуть соблазн позволить порядку зависеть от типа аргумента. Просто потому, что пара строк подряд, а затем пара логических значений выглядит лучше. На каком-то уровне это станет искусством, а не умением.

Меня не волнует утверждение о том, что вы должны обернуть все аргументы в объект, чтобы в итоге получить один аргумент. Это просто обманывает себя (это не делает метод менее сложным, у него все еще есть все эти аргументы, вы их просто спрятали). Это все еще может быть удобно, если вы передадите набор от метода к методу пару раз, рефакторинг, несомненно, станет намного проще, но с точки зрения дизайна он просто притворяется, что вы что-то делаете. Это не значит, что вы получите осмысленный объект, который представляет что-то, это просто набор аргументов, ничем не отличающийся от списка в объявлении метода. Это не сделает дядю Боба счастливым.

Мартин Маат
источник