如何运用“提取法”重构为一段代码
本文关键字:代码 一段 重构 何运用 提取 提取法 | 更新日期: 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 */),
// ...
};