我刚刚编写了一个 700 行的类。可怕。我羞愧地低下了头。它与DRY正好相反,就像英国的夏天一样。
它充满了剪切和粘贴,并在这里和那里进行了细微的调整。这使它成为重构的主要候选者。在我开始之前,我想我会问当你有很多重复时,你寻找的第一个重构机会是什么?
作为记录,我的可能正在使用:
- 泛型类和方法
- 方法重载/链接。
你的是什么?
我刚刚编写了一个 700 行的类。可怕。我羞愧地低下了头。它与DRY正好相反,就像英国的夏天一样。
它充满了剪切和粘贴,并在这里和那里进行了细微的调整。这使它成为重构的主要候选者。在我开始之前,我想我会问当你有很多重复时,你寻找的第一个重构机会是什么?
作为记录,我的可能正在使用:
你的是什么?
我喜欢在需要时开始重构,而不是一有机会就开始重构。您可能会说这在某种程度上是一种敏捷的重构方法。我什么时候觉得我需要?通常当我觉得我的代码中丑陋的部分开始传播时。我觉得丑是可以控制的,但是当他们开始有传播的冲动的时候,那就是你需要照顾生意的时候了。
您用于重构的技术应该从最简单的开始。我强烈推荐 Martin Fowler 的书。将通用代码组合到函数中、删除不需要的变量和其他简单的技术可以让您获得很多好处。对于列表操作,我更喜欢使用函数式编程习语。也就是说,我尽可能使用内部迭代器、map、filter 和 reduce(在 python 中,ruby、lisp 和 haskell 中有相应的东西),这使得代码更短,更独立。
有时,当您使用复制和粘贴代码“完成功能”时,您已经到了这样的地步,即它已被损坏和损坏,以至于任何重构尝试实际上都比在它原来的位置重构它花费的时间要长得多明显的。
以我个人的经验,我最喜欢的“消除重复的方法”是 Resharper 的“提取方法”功能(尽管这在 vanilla Visual Studio 中也可用)。
很多时候,我会看到重复的代码(我正在维护的一些遗留应用程序)不是完整的方法,而是完全独立的方法中的块。这为将这些块转化为方法提供了绝佳的机会。
Monster 类也倾向于显示它们包含多个功能。这反过来又成为将每个不同的功能分成自己的(希望更小)类的机会。
我必须重申,做所有这些(对我来说)根本不是一种愉快的体验,所以我真的宁愿在它是一个小泥球的时候做正确的事,而不是让大泥球滚动然后尝试解决这个问题。
#地区
我用它做了一个1000行的类只有一行!
严肃地说,避免重复的最佳方法是列表中涵盖的内容,以及充分利用多态性,检查您的类并发现在基类中最好的做法,以及如何分解它的不同组件一个子类。
首先,我建议您在完成该类的第一个版本时更快地进行重构。任何时候你看到重复,尽快消除它。最初这可能需要更长的时间,但我认为结果最终会变得更加清晰,并且它可以帮助您重新思考您的代码,以确保您做对了事情。
至于我最喜欢的删除重复的方式……闭包,尤其是在我最喜欢的语言(Ruby)中。它们往往是获取 2 段代码并合并相似之处的一种非常简洁的方式。当然(就像任何“最佳实践”或提示一样),这不能盲目地完成......我只是觉得当我可以使用它们时使用它们真的很有趣。
我所做的一件事是尝试制作可以在编辑器(Visual Studio)的单个页面上看到的小而简单的方法。
我从经验中了解到,简化代码可以让编译器更容易对其进行优化。方法越大,编译器就越难工作!
我最近还看到了一个大型方法导致内存泄漏的问题。基本上我有一个非常类似于以下的循环:
而(真) { var smallObject = WaitForSomethingToTurnUp(); var largeObject = DoSomethingWithSmallObject(); }
我发现我的应用程序将大量数据保存在内存中,因为即使在 smallObject 返回某些内容之前“大对象”不在范围内,垃圾收集器仍然可以看到它。
我通过将“DoSomethingWithSmallObject()”和其他相关代码移动到另一个方法轻松解决了这个问题。
此外,如果你制作小的方法,你在一个类中的重用将会大大提高。我通常会尝试确保我的任何方法都不像其他方法!
希望这可以帮助。
缺口
“在这里和那里进行细微调整的剪切和粘贴”是我通常用完全非异国情调的方法解决的那种代码重复 - 获取类似的代码块,将其提取到单独的方法中。在该代码块的每个实例中都有一点不同,将其更改为参数。
还有一些简单的技术可以删除重复的 if/else if 和 switch 块,由 Scott Hanselman 提供: http ://www.hanselman.com/blog/CategoryView.aspx?category=Source+Code&page=2
我可能会这样:
为数据结构创建自定义(私有)类型并将所有相关逻辑放入其中。字典<string, List<int>> 等。
制作保证行为的内部功能或属性。如果您不断检查可公开访问的属性的条件,则创建一个私有 getter 方法,其中包含所有检查。
拆分方法分开有太多的事情。如果你不能把一些简洁的东西放进去或者给它一个好名字,然后开始分解函数直到代码被分解(即使这些“子”函数没有在其他任何地方使用)。
如果所有其他方法都失败了,请在其上打一个 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] 并评论原因。