如何运用“提取法”重构为一段代码

本文关键字:代码 一段 重构 何运用 提取 提取法 | 更新日期: 2023-09-27 18:16:48

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

下面的c#区域在方法中出现了大约50次:

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

我想知道正确的(如果可能的话)方法来提取这个区域到一个新的方法。

正如我所说,该片段(区域)在方法中出现了大约50次。请注意#区域内的返回值(它将退出while)。

方法的结构如下:

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
        .
        .
        .
    }
}

如何运用“提取法”重构为一段代码

我不会说有一种正确的方法来重构它,只是有一些奇怪的方法来完成它,包括将它提取到一个方法中,无论是否执行控制语句,该方法都会返回true/false。你仍然需要重复50次,所以这样做没有多少好处。

我根本不建议重构那块代码。相反,我建议您围绕它重构代码。重构相邻的代码片段有时会揭示出以前无法识别的模式。

首先提取每个"…"块的方法。您现在拥有的是"调用一个方法,然后在等待取消时退出"的模式。通过将这些方法转换为委托,它们就变成了数据元素,你可以在它们上面循环。

让我们暂时假设提取的方法具有相同的签名。声明委托实例数组,在循环中执行它们,并在每次迭代结束时检查挂起的取消。因为你有一个return而不是break,你不需要做任何额外的事情来跳出内循环。

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种不同的方法。如果您预见到将来需要更多的参数变化,那么这可能不是一个好的选择。

另一种选择是使用具有相对简单签名的lambdas,并利用闭包抽象出差异。

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