Как предотвратить глубокие вмятины? [закрыто]

17

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

Тамара Вийсман
источник
2
Многие люди будут говорить о рефакторинге здесь. Может быть, это слишком много, чтобы спросить, но если вы разместили какой-то (не слишком длинный) код с глубоким отступом, и люди могли бы показать вам, как они будут его реорганизовывать. Конечно, это, вероятно, делает язык вопроса специфичным тогда ...
Paddyslacker
3
Используйте меньшую ширину вкладки.
Мипади
6
Стрелка против узора. Google это, множество советов
NimChimpsky
2
Прекратите использовать python: D
back2dos
Пришло время взглянуть на вашу логику управления и петли. Вероятно, ваш код сложнее, чем нужно, и переосмысление проблемы приведет к гораздо более короткому коду. Изучите хороший код и изучите методы.
Макнейл

Ответы:

14

Глубокий отступ обычно не является проблемой, если каждая функция / метод в вашей программе выполняет одно и только одно. Иногда может потребоваться вложить условные выражения на несколько уровней, но я могу честно сказать, что я написал код с глубоким отступом всего несколько раз за 12+ лет написания кода.

Чинмай Канчи
источник
26

Лучшее, что вы можете сделать, это извлечь методы:

int Step1(int state)
{
    if (state == 100)
    {
        return Step2(state);
    }
    else
    {
        return Step3(state);
    }
}

int Step2(int state)
{
    if (state != 100)
    {
        throw new InvalidStateException(2, state);
    }

    // ....
}
ChaosPandion
источник
2
Это также работает для сложных ifусловий. В крайнем случае вы получите исполняемый псевдокод.
Алан Плам
Другая лучшая вещь, которую мы можем сделать, это, вероятно, отбросить ненужные elseблоки.
Sepehr
16

Может быть, вы могли бы рассмотреть пункты охраны ?

вместо того

public void DoSomething(int value){
    if (someCondition){
           if(someOtherCondition){
                if(yetAnotherCondition){
                       //Finally execute some code
                }
           }
    }
} 

Делать

public void DoSomething(int value){
    if(!(someCondition && someOtherCondition && yetAnotherCondition)){
        return;
        //Maybe throw exception if all preconditions must be true
    }
    //All preconditions are safe execute code
}

Если у вас когда-нибудь будет шанс, я рекомендую вам прочитать «Code Complete» Стива Макконнелла. У него много хороших советов по этим темам.

http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=pd_sim_b_6

Для получения дополнительной информации о «пунктах охраны» см .: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses

Джейсон Туран
источник
8

Инвертировать ваши ifс.

Вместо того:

if (foo != null)
{
    something;
    something;
    if (x)
    {        
       something;
    }
    something;
}
else
{
    boohoo;
}

Я бы написал:

if (foo == null)
{
    boohoo;
    return;
}
something;
something;
if (x)
{        
   something;
}
something;

То же относится и к if- elseблокам. Если elseкороче / меньше вложенных, то верните их.

Проверьте значения параметров в одном месте

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

Конрад Моравский
источник
1
У этого стиля есть определенное имя?
Томас Лаурия
@ThomasLauria не то, что я знаю. Это просто выход рано. Ifs в начале кода, которые останавливают поток выполнения из-за невыполнения некоторых условий, также называются защитными предложениями , как указывал @JasonTuran. И это кажется настолько близким, насколько это возможно, к определенному имени.
Конрад Моравский
Несколько лет назад мой начальник сказал мне, что этот стиль был назван «линейным программированием», но я думаю, что это был его призрак;)
Томас Лаурия
4

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

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

Vaibhav
источник
2

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

Кроме того, сделайте отступ двух пробелов вместо четырех.

Хоа Лонг Там
источник
5
Как только вы перешли к этапу изменения ширины вкладки, у вас возникли серьезные проблемы ...
Дейнит,
Вкладки с двумя пробелами - трудная вещь ...
Дэвид Торнли
6
Изменение отступа - это просто способ скрыть проблему, а не решение
Murph
1

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

Обычно вместо вложенных ifs я люблю писать логические выражения:

if (foo && bar && baz) 

скорее, чем

if foo 
 if bar
   if baz
Пол Натан
источник
Проблема в том, что существуют циклы for и while, которые не подпадают под это правило.
Тамара Вийсман
@ TomWij: я не пытаюсь вызвать категорический императив о стиле.
Пол Натан
1
??? `` `` ``
Тамара Вийсман
1

Я сам в это не верил, но согласно Code Complete это подходящее место для использования break(если ваша команда на борту). Я полагаю, что это более приемлемо для программистов на C ++, хотя там, где они breakиспользуются в switchвыражениях, чем для программистов на Delphi, где они breakиспользуются только тогда, когда вам не хочется писать whileцикл.

Питер Тернер
источник
0

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

Вместо того :

 {if (networkCardIsOn() == true)
     {if (PingToServer() == true)
        {if (AccesLogin(login,pass) == true)
             {if (nextCondition == true)
                ...
         }
     }
 }

Я сейчас пишу:

 {vbContinue = true;

 if (vbContinue) {
       vbContinue = networkCardIsOn();
       if (vbContinue == false) {
             code to Handle This Error();
       } 
 }

 if (vbContinue) {
       vbContinue = PingToServer();
       if (vbContinue == false) {
             code to HandleThisError2();
       } 
 }

 if (vbContinue) {
       vbContinue = AccesLogin(login,pass);
      if (vbContinue == false) {
             HandleThisErrorToo();
       } 
 }
 ...

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

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

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

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

Пьер Ватле
источник
Можете ли вы уточнить, что «затраты на обслуживание были поделены пополам» Кроме того, как бы вы на самом деле узнали, что, если остановит выполнение?
Крис
Я сделал некоторое редактирование. Надеюсь, я отвечу на ваши вопросы ...
Пьер Уатле
2
Если вы хотите пойти просто, у вас есть строка goto error_handling.
Пол Натан
Это не приятно. Извините, но его просто нет. Тогда опять я странный
Мерф
3
вместо того, чтобы эмулировать try catch, почему бы не использовать его напрямую?
Newtopian