代码重构的艺术与实践

在多年的职业生涯中,经常需要处理他人的代码。这让培养了一些非常有用的代码维护技能。然而,能够阅读他人的代码并不能减少处理糟糕代码时的挫败感。这种挫败感不仅来自于需要修复和扩展代码,还来自于试图弄清楚代码可能出错的地方,并且还要为这些错误背后的错误思考承担后果。

更糟糕的是,一些管理者会阻碍任何重构的努力,他们将重构视为镀金行为,认为这是浪费时间和资源,而这些资源本应该用于开发新功能和保持项目按当前计划进行。事实上,代码腐烂是头痛的主要来源,是无尽bug和计划延误的根源。不解决这个问题会危及项目,给团队带来不必要的压力。但还有其他问题,一些团队成员缺乏技术技能,也不愿意学习新技能。因此,他们对腐烂的代码感到满意,因为他们更了解它,任何变化都会给他们带来压力。

在这种情况下,必须秘密地进行重构,这意味着应用程序的架构将保持不变,但一些有问题的代码文件将被重构。保持公共签名和整体愚蠢的函数,这些函数做得太多,不能立即改变,否则会引起不满。

让看看代码,好吗?从一个呈现以下模式的方法集开始: 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 + ""); 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 + ""); 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 + ""); 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 + ""); return XmlOut; } }

在.NET 3.5及以上版本中: // Now this is the repeated pattern public XmlDocument ThisMethodDoesSomethingImportant( string xmlString) { Func function = (SomeBusinessClass business) => business.SomeMethod(xmlString); return this.DoesSomethingBody(function); } private XmlDocument DoesSomethingBody(Func function) { XmlDocument XmlOut = new XmlDocument(); string Error = ""; this.DoesSomething(out Error); if (Error != "") { // Do Some Trace code here... XmlOut.LoadXml("" + 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 + ""); return XmlOut; } }

这些示例中所做的是将方法体(大部分共享代码)与初始版本分开,并将委托或匿名函数发送以处理代码的变化部分。公共方法现在只是一个创建委托并将其发送到方法体的门面。

如果方法签名中有更多的输入变量,这也很容易扩展。 public XmlDocument ThisMethodDoesSomethingImportant2( string xmlString, string par1, int par2) { Func function = (SomeBusinessClass business) => business.SomeMethodExtended(xmlString, par1, par2); return this.DoesSomethingBody(function); } public XmlDocument ThisMethodDoesSomethingImportant3( string xmlString, string par1, string par2) { Func function = delegate (SomeBusinessClass business) { business.SomeProperty = par2; return business.SomeMethodEx(xmlString, par1); }; return this.DoesSomethingBody(function); }

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485