Если утверждения в функции, каков наилучший способ
-
06-07-2019 - |
Вопрос
Если у меня есть функция с кучей условных операторов, как лучше всего ее организовать?
Меня беспокоит то, что кто-то еще войдет в код и поймет, что происходит.Хотя пример прост, представьте, что условное выражение очень сложное.
Например:
public void function(string value, string value2)
{
if (value == null)
return;
if (value2 == value)
DoSomething();
}
или
public void function(string value, string value2)
{
if (value != null)
{
if (value2 == value)
DoSomething();
}
}
или
public void function(string value, string value2)
{
if (value != null && value2 == value)
DoSomething();
}
Решение
Организуйте условия и поместите их в метод.
например, замените это:
if( a& & n || c && ( ! d || e ) && f > 1 && ! e < xyz ) {
// good! planets are aligned.
buyLotteryTicket();
} else if( ..... oh my ... ) {
}
В это:
if( arePlanetsAligned() ) {
buyLotteryTicket();
} else if( otherMethodHere() ) {
somethingElse();
}
Таким образом, не имеет значения, какой стиль вы используете (1, 2 или 3), поскольку оператор if четко описывает, какое условие проверяется.Нет необходимости в дополнительных конструкциях.
Цель состоит в том, чтобы сделать код более понятным и самодокументируемым.Если вы используете объектно-ориентированный язык программирования, вы можете использовать объект для хранения состояния (переменные) и избегать создания методов, принимающих 5–10 параметров.
Это похожие вопросы:
Лучший способ избавиться от вложенных ifs
Есть ли альтернатива этому гиперидентифицированному коду?
Вторая ссылка показывает более полный и сложный способ превратить ужасный кошмар для всех сопровождающих в самодокументируемый код.
Он показывает, как это преобразовать:
public String myFunc(SomeClass input)
{
Object output = null;
if(input != null)
{
SomeClass2 obj2 = input.getSomeClass2();
if(obj2 != null)
{
SomeClass3 obj3 = obj2.getSomeClass3();
if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
{
SomeClass4 = obj3.getSomeClass4();
if(obj4 != null)
{
int myVal = obj4.getSomeValue();
if(BAD_VALUE != myVal)
{
String message = this.getMessage(myVal);
if(MIN_VALUE <= message.length() &&
message.length() <= MAX_VALUE)
{
//now actually do stuff!
message = result_of_stuff_actually_done;
}
}
}
}
}
}
return output;
}
в это:
if ( isValidInput() &&
isRuleTwoReady() &&
isRuleTreeDifferentOf( BAD_OBJECT ) &&
isRuleFourDifferentOf( BAD_VALUE ) &&
isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) {
message = resultOfStuffActuallyDone();
}
Другие советы
Я предпочитаю первый вариант - быстро провалиться чище, яснее и легче читается и понимается.
Я понимаю, что это не провал, но эта концепция по-прежнему актуальна.Мне очень не нравятся вложенные if
заявления вообще.
Вы можете рассмотреть защитное программирование, чтобы гарантировать выполнение контракта на функциональность методов.
public void function(string value, string value2)
{
if (string.IsNullOrEmpty(value1)) throw new ArgumentNullException("value1", "value 1 was not set");
if (string.IsNullOrEmpty(value2)) throw new ArgumentNullException("value2", "value 2 was not set");
DoSomething();
}
Рефакторинг этого материала в отдельную функцию.Гораздо лучше прочитать описательное имя функции, чем кучу логических выражений.
// leave complex conditional code out, so that we can focus on the larger problem in the function
public void function(string value, string value2)
{
if (MyDescriptiveTestName)
{
DoSomething();
}
}
// encapsulate complex conditional code so that we can focus solely on it.
private bool MyDescriptiveTestName(string value, string value2)
{
if (value != null && value2 == value)
{
return true;
}
return false;
}
Могу ли я порекомендовать книгу Чистый код Роберт С.Martin, который предоставляет отличный набор эвристик для написания читаемого и поддерживаемого кода.
Теперь другой вариант — извлечь условное выражение в другую приватную функцию и назвать его так, чтобы оно описывало ваше намерение.Он не слишком хорошо работает с предоставленным кодом, поскольку он общий, но будет выглядеть примерно так:
public void function(string value, string value2)
{
if (valuesAreValidAndEqual(value, value2))
{
DoSomething();
}
}
private void valuesAreValidAndEqual(string value, string value2)
{
return value != null && value2 == value;
}
Очевидно, что это более полезно, если имена переменных и имена функций связаны с вашим доменом.
Если у вас есть функция с большим количеством условий, я бы использовал switch
утверждение, а не «если».Я мог бы также разбить детали на несколько функций (или даже классов), если это возможно.
Связанные статьи о переполнении стека:
Мне нравится третий вариант, но это сильно зависит от языка.Вы предполагаете, что в третьем случае первая часть оператора завершится неудачей и не выполнится вторая.Это зависит от языка.Я знаю, что большинство языков на основе C делают это, но поскольку вы не указали тот, который является потенциальной проблемой.Там может быть один, о котором я не знаю, который не имеет концепции короткого замыкания.
То, что вы заранее думаете о читаемости кода, — это полдела.
Что касается того, какой из ваших примеров наиболее читаем, это довольно субъективно.
Мое мнение по поводу приведенных примеров:
- Лично я думаю, что первый пример легче всего следовать.
- Минимизация уровней гнездования и/или количества условных условий обычно повышает читабельность.
- Некоторые будут спорить против нескольких точек выхода из метода (например, пример 1), но я думаю, что t становится проблемой только тогда, когда метод становится действительно долго.На самом деле это не так уж важно, если вы просто проверяете входные данные и быстро терпят неудачу.
Я предпочитаю второй вариант.Лично, когда я читаю такой код, я имею в виду условия, при которых мне придется войти на каждый вложенный уровень.В первом примере я, вероятно, забыл бы, что первое условие (значение == null равно false) продолжает выполняться.Третий тоже неплох, но мне больше нравится второй.
Тот факт, что у вас так много операторов в функции, является признаком того, что функцию следует разделить на более мелкие.
Не существует ЛУЧШЕГО способа размещения утверждений if, я думаю, что ответы субъективны.
Ясность часто является качеством, о котором трудно судить.То, что ясно вам, когда вы пишете фрагмент кода, может быть совершенно непонятным для кого-то другого — или даже для вас самих по прошествии достаточного времени.
Вот мои личные практические правила при создании функции со множеством условных проверок:
- По возможности группируйте условия в логические единицы.
- Используйте четко названные промежуточные значения, чтобы упростить чтение и отладку.
- Избегайте повторения одних и тех же логических конструкций более одного раза (см. выше).
- По возможности реорганизуйте сложные или дорогостоящие условия в отдельные функции.
- Когда это возможно, используйте условия раннего выхода в верхней части метода для выхода, но...
- Избегайте использования операторов возврата по всему телу метода.
- Не полагайтесь на приоритет операторов, используйте круглые скобки для группировки операторов.
- Не полагайтесь на отступы, используйте блоки ({...}) даже с одним оператором, это упрощает сопровождение.