Статьи

Откуда берутся условные выражения?

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

Для этого я перечислил происхождение условных обозначений, о которых я мог подумать. Это дурацкое поручение? Возможно, но все равно давайте попробуем. Пожалуйста, прокомментируйте, можете ли вы найти что-то, что я пропустил, и давайте посмотрим, сможем ли мы составить «полный» список…

  1. Проверка значения, возвращенного мне из кода, которым я владею.
    В этом суть запаха Null Check, а в более общем смысле — Connascence of Value . Например, посмотрите этот (намеренно плохой) код из Книги Рефакторинга Уильяма К. Уэйка:
    public class Report {
      public static void report(Writer out, List<Machine> machines, Robot robot) throws IOException
      {
        //...
    
        out.write("Robot");
        if (robot.location() != null)
          out.write(" location=" + robot.location().name());
    
        if (robot.bin() != null)
          out.write(" bin=" + robot.bin());
    
        //...
      }
    }
    
  2. Проверка значения, возвращенного мне из кода, которым я не владею.
    Например, в проектах Rails мы часто видим код такого рода:
    if User.exists?(:user_id => current_user.id)
      # ...
    else
      # ...
    end
    
  3. Проверка параметра, переданного мне кодом, которым я владею.
    Всякий раз, когда я пишу метод, который принимает логический параметр , я открываю себя для запаха управляющей пары. Вот пример из блога Петра Солнца :
    def say(sentence, loud = false)
      if loud
        puts sentence.upcase
      else
        puts sentence
      end
    end
    
  4. Проверка параметра, переданного мне кодом, который мне не принадлежит
    Вот пример из документации по jQuery:
    <script>
    var xTriggered = 0;
    $( "#target" ).keypress(function( event ) {
      if ( event.which == 13 ) {
         event.preventDefault();
      }
      xTriggered++;
      var msg = "Handler for .keypress() called " + xTriggered + " time(s).";
      $.print( msg, "html" );
      $.print( event );
    });
    </script>
    
  5. Проверка моего собственного состояния или атрибутов
    Вот (намеренно плохой) пример из Рефакторинга Мартина Фаулера:
    class Employee {
      private int _type;
      static final int ENGINEER = 0;
      static final int SALESMAN = 1;
      static final int MANAGER = 2;
    
      Employee (int type) {
        _type = type;
      }
    
      int payAmount() {
        switch (_type) {
          case ENGINEER:
            return _monthlySalary;
          case SALESMAN:
            return _monthlySalary + _commission;
          case MANAGER:
            return _monthlySalary + _bonus;
          default:
            throw new RuntimeException("Incorrect Employee");
        }
      }
    }
    
  6. Проверка значения, которое я установил ранее в этом методе.
    Например:
    public String format(List&lt;String&gt; items) {
      StringBuffer s = new StringBuffer();
      boolean first = true;
      for (String item : items) {
        if (first)
          first = false;
        else
          s.append(", ");
        s.append(item);
      }
      return s.toString();
    }
    
  7. Проверка типа исключения, сгенерированного моим кодом.
    Вот пример из документации по языку PHP:
    <?php
    function inverse($x) {
        if (!$x) {
            throw new Exception('Division by zero.');
        }
        return 1/$x;
    }
    
    try {
        echo inverse(5) . "\n";
        echo inverse(0) . "\n";
    } catch (Exception $e) {
        echo 'Caught exception: ',  $e->getMessage(), "\n";
    }
    
    // Continue execution
    echo "Hello World\n";
    ?>
    
  8. Проверка типа исключения, генерируемого кодом, которым я не владею.
    Мы часто находим такой код в контроллерах Rails:
    def delete
      schedule_id = params[:scheduleId]
      begin
        Schedules.delete(schedule_id)
      rescue ActiveRecord::RecordNotFound
        render :json => "record not found"
      rescue ActiveRecord::ActiveRecordError
        # handle other ActiveRecord errors
      rescue # StandardError
        # handle most other errors
      rescue Exception
        # handle everything else
      end
      render :json => "ok"
    end
    

Этот список похож на полный? Может ли это быть?

Интересно, может ли быть полезным следующий шаг — записать некоторые общие стратегии удаления каждого из этих видов…?