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


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);
    }

    // ....
}

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

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

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


2

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

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


5
Как только вы перешли к этапу изменения ширины вкладки, у вас возникли серьезные проблемы ...
Дейнит,

Вкладки с двумя пробелами - трудная вещь ...
Дэвид Торнли

6
Изменение отступа - это просто способ скрыть проблему, а не решение
Murph

1

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

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

if (foo && bar && baz) 

скорее, чем

if foo 
 if bar
   if baz

Проблема в том, что существуют циклы for и while, которые не подпадают под это правило.
Тамара Вийсман

@ TomWij: я не пытаюсь вызвать категорический императив о стиле.
Пол Натан


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.