Как рефакторинг условной логики в JavaScript? - PullRequest
3 голосов
/ 22 сентября 2010

Я унаследовал большое приложение JavaScript (MooTools) для просмотра и настройки продуктов.

В нем есть функция, которая отслеживает настроенное состояние продуктов.Другие функции вызываются в зависимости от того, в каком состоянии находится продукт.

Я расширил его, добавив некоторые новые функции, но закончил путаницей вложенных условных выражений внутри большого оператора switch.

Мой вопрос, как вы рефакторинг условной логики?Существует ли общий (или специфичный для JS) шаблон / метод для отслеживания состояния?

Обновление: вот псевдо-версия функции для иллюстрации проблемы:

switchToVariation: function(link) {

  // set the variables      
  // before switch, set state    

  switch ( nextType ) {   
    case 'type1':
      if ( prevType == 'type1' ) {        
        if ( nextIsSameVariation ) {            
          // do stuff                                           
        } else {                                                                              
          if ( nextIsSomethingElse ) {              
            // do stuff
          }          
          // more stuff                      
        }
      } else {        
        // different stuff        
      }
      break;

    case 'type2':        
      if ( prevWasType1 ) {
        // stuff
      } else {
        if (nextIsSameStyle) {
          // stuff
        }
        // more stuff
      }    
      break;    

    default:                       
      // default stuff        
  }          

  // after switch, set state

}

Ответы [ 2 ]

3 голосов
/ 22 сентября 2010

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

Трудно быть более конкретным, не видя примера, но вы сказали, что у вас есть "... беспорядок условий внутри большого оператора switch ...", который определенно дает возможность для улучшения: вы можете перемещать содержимое каждый случай в свою функцию.

Если вы сделаете это, у вас есть два варианта: либо заставить функции работать исключительно с аргументами, которые вы передаете в них, что стремится сделать вещи более модульными, либо сделать замыкания функций, управляющие данными во внешней функции. Я бы сказал, что последний вариант менее идеален & mdash; для этих целей & mdash; но это может быть быстрее, и это будет "быстрый выигрыш".

Скажем, у нас изначально было:

function myBigFunction() {
    var v1, v2, v3;

    /* ...set v1, v2, and v3 to various things...*/

    switch (condition) {
        case foo:
            /* ...do lots of stuff involving v1 and v2...*/
            break;

        case bar:
            /* ...do lots of stuff involving v1 only...*/
            break;

        case charlie:
            /* ...do lots of stuff involving v2 and v3...*/
            break;
    }
}

Тогда:

Первый вариант: полностью независимые функции :

function myBigFunction() {
    var v1, v2, v3;

    /* ...set v1, v2, and v3 to various things...*/

    switch (condition) {
        case foo:
            doFooStuff(v1, v2);
            break;

        case bar:
            doBarStuff(v1);
            break;

        case charlie:
            doCharlieStuff(v2, v3);
            break;
    }
}

function doFooStuff(v1, v2) {
}

function doBarStuff(v1) {
}

function doCharlieStuff(v2, v3) {
}

... и, конечно, есть вероятность, что вам нужно, чтобы эти функции возвращали что-то, а затем обрабатывали это, например:

    case foo:
        v1 = doFooStuff(v1, v2);
        break;

... если doFooStuff необходимо обновить v1. Если требуется обновить v1 и v2, вы можете вернуть массив или создать свойства v1 и v2 для объекта, который вы передаете в doFooStuff и т. Д. Вот пример использования свойств: Замените Свойства v1 и v2 с объектом (vdata), который имеет свойства v1 и v2 :

var vdata = {
    v1: "someInitialValue",
    v2: "someInitialValue"
};

... тогда ваш звонок на doFooStuff:

    case foo:
        doFooStuff(vdata);
        break;

... что позволяет doFooStuff обновлять v1 и v2. Но будьте осторожны, вы не хотите создавать кухонную раковину со всеми переменными myBigFunction, это принесло бы в жертву модульность.

Второй вариант: использование замыканий и непосредственная работа с данными родительской функции :

function myBigFunction() {
    var v1, v2, v3;

    /* ...set v1, v2, and v3 to various things...*/

    switch (condition) {
        case foo:
            doFooStuff();
            break;

        case bar:
            doBarStuff();
            break;

        case charlie:
            doCharlieStuff();
            break;
    }

    function doFooStuff() {
        // work with v1 and v2 directly
    }

    function doBarStuff() {
        // work with v1 directly
    }

    function doCharlieStuff() {
        // work with v2 and v3 directly
    }
}

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

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

2 голосов
/ 22 сентября 2010

Проверьте шаблон состояния .

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

...