我有一个执行3个任务的大型方法,每个任务都可以提取到一个单独的函数中。如果我将为每个任务添加其他功能,那么它会使我的代码变得更好还是更坏,为什么?
显然,它将在主函数中减少代码行的数量,但是将有附加的函数声明,因此我的类将具有附加的方法,我认为这不好,因为这会使类变得更加复杂。
我应该在编写所有代码之前执行此操作,还是在完成所有操作然后提取函数之前将其保留?
我有一个执行3个任务的大型方法,每个任务都可以提取到一个单独的函数中。如果我将为每个任务添加其他功能,那么它会使我的代码变得更好还是更坏,为什么?
显然,它将在主函数中减少代码行的数量,但是将有附加的函数声明,因此我的类将具有附加的方法,我认为这不好,因为这会使类变得更加复杂。
我应该在编写所有代码之前执行此操作,还是在完成所有操作然后提取函数之前将其保留?
Answers:
这是我经常链接的书,但在这里我再去一遍:罗伯特·C·马丁的清洁代码,第3章,“函数”。
显然,它将在主函数中减少代码行的数量,但是将有附加的函数声明,因此我的类将具有附加的方法,我认为这不好,因为这会使类变得更加复杂。
您喜欢阅读带有+150行的函数,还是调用3 +50行函数的函数?我想我更喜欢第二种选择。
是的,从某种意义上说,它将使您的代码更“可读”。使函数执行仅一件事,它们将更易于维护并为其生成测试用例。
另外,我从上述书籍中学到的一件非常重要的事情:为您的功能选择正确的名称。函数越重要,名称就应该越精确。不必担心名称的长度,如果必须调用FunctionThatDoesThisOneParticularThingOnly
,则以这种方式命名。
在执行重构之前,请编写一个或多个测试用例。确保它们起作用。重构完成后,您将能够启动这些测试用例,以确保新代码正常工作。您可以编写其他“较小”的测试,以确保您的新功能可单独很好地执行。
最后,这与我刚刚写的内容并不矛盾,问问自己是否真的需要进行此重构,请查看“ 何时重构?” 的答案。(此外,搜索关于“重构”的问题,还有更多,答案很有趣)
我应该在编写所有代码之前先执行此操作,还是在完成所有操作然后提取函数之前将其保留?
如果代码已经存在并且可以正常工作,而您的下一个发布时间紧迫,请不要触摸它。否则,我认为应该在可能的情况下使小型函数运行,因此,应在有可用时间的时候进行重构,同时确保一切都像以前那样工作(测试用例)。
您提出的问题不是编码,约定或编码实践的问题,而是可读性和文本编辑器显示您编写的代码的方式的问题。帖子中也出现了同样的问题:
可以将长函数和方法拆分为较小的函数,即使它们不会被其他任何东西调用也可以吗?
当实现一个大型系统以封装它所组成的不同功能时,将一个功能分为多个子功能是有意义的。无论如何,迟早您会发现自己拥有许多重要功能。如果将它们保留为单个长函数,或者将它们拆分为较小的函数,则其中一些是无法阅读且难以维护的。对于在系统的其他任何地方都不需要执行操作的功能,尤其如此。让我们选择这么长的功能之一,并从更广泛的角度考虑它。
优点:
相反:
现在,让我们想象一下将long函数拆分为几个子函数,并以更广阔的前景对其进行研究。
优点:
相反:
两种解决方案都有利有弊。实际的最佳解决方案是让编辑器进行扩展,内联,并在整个深度内扩展每个函数的调用。这将使子功能拆分为唯一的最佳解决方案。
对我来说,将代码块提取到函数中有四个原因:
您正在重用它:您只是将代码块复制到剪贴板中。不仅将其粘贴,还可以将其放入函数中,并在两侧将其替换为函数调用。因此,每当需要更改该代码块时,只需更改该单个函数即可,而无需在多个位置更改代码。因此,无论何时复制代码块,都必须创建一个函数。
这是一个回调:这是事件处理程序或库或框架调用的某种用户代码。(我很难想象没有功能。)
您相信它会在当前项目或其他地方被重用:您刚刚编写了一个块,用于计算两个数组的最长公共子序列。即使您的程序仅调用一次此功能,我也相信最终在其他项目中也将需要此功能。
您需要自我记录的代码:因此,与其在一段概述其功能的代码块上写一行注释,不如将整个内容提取到一个函数中,然后将其命名为要写入注释中的内容。尽管我不喜欢这个,但是因为我喜欢写出所用算法的名称,选择该算法的原因等。那么函数名可能太长了...
旁白:我是为回应达林的问题(现已关闭)而写的,但我仍然认为这可能对某人有所帮助,因此请继续
我认为雾化函数的原因有2倍,正如@jozefg所提到的,取决于所使用的语言。
关注点分离
这样做的主要原因是将不同的代码段分开,因此任何不直接对函数的期望结果/意图作出贡献的代码块都是一个单独的问题,可以将其提取出来。
假设您有一个后台任务也更新了进度条,则进度条更新与长时间运行的任务没有直接关系,因此即使它是使用进度条的唯一代码,也应将其提取。
在JavaScript中说您有一个函数getMyData(),该函数1)从参数构建一条肥皂消息,2)初始化服务引用,3)用肥皂消息调用服务,4)解析结果,5)返回结果。看起来很合理,我已经多次编写了这个确切的函数-但实际上可以将其拆分为3个私有函数,仅包括3和5的代码(如果有的话),因为其他任何代码都不直接负责从服务获取数据。
改进的调试体验
如果您具有完全原子的功能,则堆栈跟踪将成为任务列表,列出所有成功执行的代码,即:
会更有趣,然后发现获取数据时出错。但是,某些工具对于调试详细的调用树甚至更为有用,例如Microsoft的Debugger Canvas。
我也理解您的担心,因为遵循这种方式编写的代码可能很困难,因为到了最后,您确实需要在单个文件中选择函数的顺序,因为您的调用树将比这复杂得多。 。但是,如果函数命名正确(智能允许我在任何函数中使用3-4个大写字母,请不要让我慢下来),并在文件顶部使用公共接口进行结构化,则您的代码将像伪代码一样读取,到目前为止,这是获得代码库高级理解的最简单方法。
仅供参考-这是“按我说的不做”的事情之一,保持代码原子性是没有意义的,除非您无情地与恕我直言保持一致,我不是。