使用委托重构复制/粘贴代码






4.80/5 (5投票s)
一种在 C# 中重构复制/粘贴模式的策略性解决方案。
引言
在本文中,我将提出一种针对 C# 中复制/粘贴模式的策略性解决方案,其中方法共享大部分代码,只有一两行不同。
背景
在我的大部分职业生涯中,我都不得不处理别人的代码,这让我发展了一些非常有用的代码维护技巧。但是,阅读别人代码的能力并不能减轻处理糟糕代码的挫败感。令人沮丧的原因不仅在于必须修复和扩展它,还在于试图弄清楚它会在哪里出错,并对背后的糟糕想法的后果负责。
更糟糕的是,一些管理者阻碍任何重构工作,将其贬低为镀金,浪费时间和资源,而这些时间和资源本应用于开发新功能并保持项目与当前进度保持一致。
事实上,代码腐烂是头痛的主要来源,是无休止的错误和进度延误的来源。不处理这个问题会危及项目,并使团队承受不必要的压力。但还有其他问题,一些团队成员缺乏技术技能和学习新技能的意愿。因此,他们对腐烂的代码感到满意,因为他们更能理解它,任何改变都会让他们感到压力。
在这种情况下,重构必须秘密进行,这意味着应用程序的架构将保持不变,但一些麻烦的代码文件将被重构。保持公共签名和总体上愚蠢的功能,这些功能做的事情太多,无法立即更改,而不会引起愤怒的声音。
让我们开始编写代码吧?
我们从一组呈现以下模式的方法开始
public XmlDocument ThisMethodDoesSomethingImportant(string xmlString)
{
XmlDocument XmlOut = new XmlDocument();
string Error = "";
this.DoesSomething(out Error);
if (Error != "")
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
SomeBusinessClass business = new SomeBusinessClass(this.SomeInitialVar,
this.SomeOtherVarThatIsInitiadedOnDoesSomething);
try
{
// this is the only that changes
XmlOut = business.SomeMethod(xmlString);
}
catch (Exception ex)
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
return XmlOut;
}
这个类有很多方法,全部或大部分都只是一个副本;挑战在于删除大部分复制的代码,并保持其形式,同时获得更易于维护的东西。
解决方案是在 .NET 2.0 中使用委托,在 3.5 及更高版本中使用 Lambda 函数,但首先,我们需要将方法体与公共方法调用分开。
在 .NET 2.0 中
private delegate XmlDocument DelegatedFunction(SomeBusinessClass business);
// Now this is the repeated pattern
public XmlDocument ThisMethodDoesSomethingImportant(string xmlString)
{
DelegatedFunction function = delegate(SomeBusinessClass business)
{
// changed method here
return business.SomeMethod(xmlString);
};
return this.DoesSomethingBody(function);
}
private XmlDocument DoesSomethingBody(DelegatedFunction function)
{
XmlDocument XmlOut = new XmlDocument();
string Error = "";
this.DoesSomething(out Error);
if (Error != "")
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
SomeBusinessClass business = new SomeBusinessClass(this.SomeInitialVar,
this.SomeOtherVarThatIsInitiadedOnDoesSomething);
try
{
// here is the change
return function(business);
}
catch (Exception ex)
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
}
在 .NET 3.5 及更高版本中
// Now this is the repeated pattern
public XmlDocument ThisMethodDoesSomethingImportant(string xmlString)
{
Func<SomeBusinessClass,XmlDocument> function =
(SomeBusinessClass business) => business.SomeMethod(xmlString);
return this.DoesSomethingBody(function);
}
private XmlDocument DoesSomethingBody(Func<SomeBusinessClass,XmlDocument> function)
{
XmlDocument XmlOut = new XmlDocument();
string Error = "";
this.DoesSomething(out Error);
if (Error != "")
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
SomeBusinessClass business = new SomeBusinessClass(this.SomeInitialVar,
this.SomeOtherVarThatIsInitiadedOnDoesSomething);
try
{
// here is the change
return function(business);
}
catch (Exception ex)
{
// Do Some Trace code here...
XmlOut.LoadXml("<Error>" + Error + "</Error>");
return XmlOut;
}
}
这些示例中所做的是将方法体(包含大部分共享代码)与初始版本分开,并发送委托或匿名函数来处理代码的变体部分。公共方法现在只是一个外观,用于创建委托并将其发送到方法体。
如果方法签名上有更多的输入变量,也可以很容易地扩展它。
public XmlDocument ThisMethodDoesSomethingImportant2(string xmlString,
string par1, int par2)
{
Func<SomeBusinessClass,XmlDocument> function =
(SomeBusinessClass business) => business.SomeMethodExtended(xmlString, par1, par2);
return this.DoesSomethingBody(function);
}
public XmlDocument ThisMethodDoesSomethingImportant3(string xmlString,
string par1, string par2)
{
Func<SomeBusinessClass,XmlDocument> function = delegate(SomeBusinessClass business)
{
business.SomeProperty = par2;
return business.SomeMethodEx(xmlString, par1);
};
return this.DoesSomethingBody(function);
}
关注点
这种重构仍然留下很多丑陋的东西,但让您有时间思考更好的解决方案来重构整个类和相邻的类。希望这可以帮助您的工作,而不会引起同事和经理的愤怒。
历史
- 第一次提交 - 2010 年 3 月 11 日。