如何应用"提取方法"重构一段代码

时间:2016-11-07 23:17:25

标签: c# .net refactoring .net-2.0

我们有一个遗留应用程序,它有一个1200行长的方法(一个线程的run方法)。该方法大多是单个while(true),包含一长串句子。

以下C#区域在方法中存在约50次:

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

我想知道将此区域提取到新方法的正确方法(如果可能)。

正如我所说,该片段(Region)在该方法中出现约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

        .
        .
        .

    }

}

1 个答案:

答案 0 :(得分: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;
        }
    }
}

&#34;调用方法,然后在取消等待时退出&#34;现在与它要拨打的方法列表分开,你只需要进行一次取消检查。方法列表预先建立,然后输入该块。你可以采取额外的步骤,将while循环解压缩到自己的方法,然后将委托列表传递给它。另一方面,这可能会使你的需求太过分了。

如果提取的方法具有不同的签名,那么它并不简单,但您确实有一些选项。您可以调整方法以采用相同的参数,让它们忽略它们不能使用的参数。但是,参数太多,可维护性可能会开始远离您,特别是如果您必须调整50种不同的方法。如果您预计将来需要更多的参数变化,这可能不是一个好的选择。

另一种选择是使用具有相对简单签名的lambda,并利用闭包来消除差异。

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 */),
    // ...
};