2016-11-07 10 views
0

У нас есть унаследованное приложение, которое имеет метод линии длиной 1200 строк (метод запуска потока). Метод в основном является синглом while (true), содержащим длинную последовательность предложений.Как применить рефакторинг «Метод извлечения» к фрагменту кода

Следующая C# регион присутствует около 50 раз в методе:

#region Cancel pending 
    if (backgroundWorkerPrincipal.CancellationPending) 
    { 
     if (CanCancelThread) 
     { 
      ev.Cancel = true; 
      return; 
     } 
    } 
#endregion 

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

Как я уже сказал, этот фрагмент (Регион) появляется около 50 раз в рамках метода. Обратите внимание на возврат в #region (который выйдет из него).

Так метод имеет следующую структуру:

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs ev) 

    while(true) { 

     ... 

     #region Cancel pending 
      if (backgroundWorkerPrincipal.CancellationPending) 
      { 
       if (CanCancelThread) 
       { 
        ev.Cancel = true; 
        return; 
       } 
      } 
     #endregion 

     ... 

     #region Cancel pending 
      if (backgroundWorkerPrincipal.CancellationPending) 
      { 
       if (CanCancelThread) 
       { 
        ev.Cancel = true; 
        return; 
       } 
      } 
     #endregion 

       ... 

     #region Cancel pending 
      if (backgroundWorkerPrincipal.CancellationPending) 
      { 
       if (CanCancelThread) 
       { 
        ev.Cancel = true; 
        return; 
       } 
      } 
     #endregion 

       ... 

     #region Cancel pending 
      if (backgroundWorkerPrincipal.CancellationPending) 
      { 
       if (CanCancelThread) 
       { 
        ev.Cancel = true; 
        return; 
       } 
      } 
     #endregion 

     ... 

     #region Cancel pending 
      if (backgroundWorkerPrincipal.CancellationPending) 
      { 
       if (CanCancelThread) 
       { 
        ev.Cancel = true; 
        return; 
       } 
      } 
     #endregion 

     . 
     . 
     . 

    } 

} 
+0

Должно ли это быть помечено как java? – nbrooks

+0

@nbrooks хороший улов. –

+0

В .net 4.5 этот 'BackgroundWorker' можно заменить' Task', 'async' и' await'. Попробуйте. –

ответ

3

Я бы не сказал, что есть правильный способ реорганизовать его, только некоторые напуганные способов сделать это, которые будут работать, в том числе извлечение его метод, который возвращает true/false независимо от того, выполнять ли оператор управления. Вам все равно придется повторять его 50 раз, так что этого мало.

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

Начните с извлечения методов для каждого из этих блоков «...». То, что у вас есть сейчас, - это шаблон «вызов метода, а затем сбегающий, если отмена ожидается». Преобразуя эти методы в делегаты, они становятся элементами данных, и вы можете их перебирать.

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

var extractedMethods = new Func<State, DoWorkEventArgs, State>[] 
{ 
    DoStep1, 
    DoStep2, 
    DoStep3, 
    // ... 
}; 

while (true) 
{ 
    foreach (Func<State, DoWorKEventArgs, State> fn in extractedMethods) 
    { 
     state = fn(state, ev); 

     if (backgroundWorkerPrincipal.CancellationPending && CanCancelThread) 
     { 
      ev.Cancel = true; 
      return; 
     } 
    } 
} 

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

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

Другим вариантом является использование лямбда с относительно простой подписью и использование закрытий для абстрагирования различий.

var extractedMethods = new Func<State, DoWorKEventArgs, State>[] 
{ 
    (st, ev) => RunStep1(st, ev /*, parameters specific to RunStep1 */), 
    (st, ev) => RunStep2(st, ev /*, parameters specific to RunStep2 */), 
    (st, ev) => RunStep3(st, ev /*, parameters specific to RunStep3 */), 
    // ... 
};