Однострочные функции, вызываемые только один раз

Рассмотрим функцию без параметров ( edit: не обязательно), которая выполняет одну строку кода и вызывается только один раз в программе (хотя не исключено, что она понадобится снова в будущее).

Он может выполнять запрос, проверять некоторые значения, делать что-то с использованием регулярного выражения ... что-то неявное или «взломанное».

Обоснованием этого было бы избежать трудно читаемых оценок:

 if (getCondition()) {
    // do stuff
}

где getCondition() - это однострочная функция.

Мой вопрос просто: это хорошая практика? Мне кажется хорошо, но я не знаю о долгосрочной перспективе ...

115 голосов | спросил vemv 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 18:34:08 +0400 2011, 18:34:08

11 ответов


236

Зависит от одной строки. Если строка является читаемой и краткой сама по себе, функция может не понадобиться. Упрощенный пример:

 void printNewLine() {
  System.out.println();
}

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

 boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
ответил Péter Török 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 18:36:41 +0400 2011, 18:36:41
65

Да, это можно использовать для удовлетворения лучших практик. Например, лучше иметь четко обозначенную функцию, хотя бы одну строчку длиной, чем иметь такую ​​строку кода в более крупной функции, и ей нужен однострочный комментарий, объясняющий, что он делает. Кроме того, соседние строки кода должны выполнять задачи с одинаковым уровнем абстракции. Контрпример будет что-то вроде

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

В этом случае, безусловно, лучше перемещать среднюю строку в функцию с разумным именем.

ответил Kilian Foth 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 18:39:03 +0400 2011, 18:39:03
36

Я думаю, что во многих случаях такая функция является хорошим стилем, но вы можете рассматривать локальную логическую переменную как альтернативу в случаях, когда вам не нужно использовать это условие где-то в других местах, например:

 bool someConditionSatisfied = [complex expression];

Это даст подсказку для чтения кода и избавит вас от введения новой функции.

ответил cybevnm 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 19:15:34 +0400 2011, 19:15:34
23

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

Следуя примеру Питера, если этот

 boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

становится этим

 boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

Вы делаете одно редактирование, и оно обновляется повсеместно. Удобство в обслуживании, это плюс.

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

ответил Stephen 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 19:13:28 +0400 2011, 19:13:28
5

В дополнение к читабельности (или дополнению) это позволяет писать функции на должном уровне абстракции.

ответил tylermac 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 22:00:19 +0400 2011, 22:00:19
4

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

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

ответил Falcon 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 18:38:17 +0400 2011, 18:38:17
3

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

ответил WojonsTech 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 13 Sep 2011 09:36:33 +0400 2011, 09:36:33
3

Я сделал эту точную вещь совсем недавно в приложении, которое я рефакторинг, чтобы четко указать фактическое значение комментариев кода без комментариев:

 protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}
ответил joshperry 12 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 12 Sep 2011 22:33:45 +0400 2011, 22:33:45
0

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

ответил Andrew 16 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 16 Sep 2011 08:15:07 +0400 2011, 08:15:07
0

Уже есть много хороших ответов, но есть специальный случай, который стоит упомянуть .

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

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

 def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

также может быть изменен на

 def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) , вы должны быть действительно уверены в этом (см. YAGNI )

ответил Wolf 9 Jpm1000000pmFri, 09 Jan 2015 19:08:49 +030015 2015, 19:08:49
0

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

 someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

IMHO это хороший компромисс, потому что он дает вам возможность чтения, не имея сложного выражения, загромождающего условие if, избегая при этом загромождения пространства имен global /package с помощью ярких ярлыков. Он имеет дополнительное преимущество в том, что функция «определение» правильна там, где ее используют, что позволяет легко модифицировать и читать определение.

Это не только предикатные функции. Мне нравится также комбинировать повторяющуюся котельную плиту в небольших функциях, подобных этому (она отлично работает для генерации списков pythonic без загромождения синтаксиса скобки). Например, следующий упрощённый пример при работе с PIL в python

 #goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]
ответил crasic 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 13 Sep 2011 04:43:50 +0400 2011, 04:43:50

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132