因为高的圈复杂度是有害的,通过创建子函数来降低它是有益的。然而,它可能会导致长调用队列,并导致一些超级嵌套函数,这也会让其他程序员感到难以阅读。
什么是有益的解决方案,或者我们应该在哪里为调用队列的深度设定底线?是否有任何度量或工具来测试函数的调用深度?
任何帮助或想法都是值得感激的。
发布于 2016-07-28 18:58:12
将代码提取到子函数并不能降低程序的圈复杂度.当然,如果将某些复杂性提取到单独的函数中,它可能会降低单个函数的圈复杂度,但这只是在移动复杂性,这并不能改善程序本身。
函数的高度圈复杂度可能是一个警告信号,表明函数的编写方式过于复杂。如果是这样的话,您应该考虑逻辑是否可以简化。
当然,代码可能很复杂,因为它解决的问题很复杂。你应该寻找意外的复杂性,复杂性,这是不必要的。一个典型的例子:
bool isZero = false;
if (x==0) {
isZero = true;
} else if (x > 0 || x < 0) {
isZero = false;
} else {
Logger.LogError("Invalid value of x");
throw new FileNotFoundException();
}解决这一意外复杂性的方法不是将其提取为单独的函数,而是将其重写为:
bool isZero = x==0;发布于 2016-07-28 17:42:26
高度的圈复杂度是否有害?您可以这样说,好像这是一个不言自明的事实,但重要的是要记住,在许多情况下,您在代码中建模的真实世界问题是一个复杂的问题。
我唯一一次听到这样的说法,即圈复杂度是要最小化的(或者实际上是人们谈论的事情)是在单元测试的背景下。如果这正是你问题的背景,詹姆斯·科普林( James )说得比我更好:
我在北欧有一个客户,要求开发人员拥有40%的代码覆盖率,级别1的软件成熟度,60%的级别2和80%的Level 3,而一些开发人员则渴望100%的代码覆盖率。没问题!你可能会认为一个有分支和循环的相当复杂的过程会提供一个挑战,但这只是一个除法的问题。80%覆盖率不可能的大函数被分解为许多小函数,其中80%的覆盖率是微不足道的。这提高了整个公司衡量其团队在一年内的成熟度,因为你肯定会得到你所得到的回报。当然,这也意味着函数不再封装算法。根据前面的代码行和在执行过程中遵循的代码行,不再可能对代码行的执行上下文进行推理,因为这些代码行不再与您所关注的代码行相邻。这个序列转换现在发生在一个多态函数调用--一个超星系的GOTO上.但如果你只关心分支机构,那就无所谓了。
-- 为什么大多数单元测试都是浪费的
在这种情况下,记住一句著名的爱因斯坦的名言是很好的:“让一切尽可能简单,但不简单。”将次要指标(如圈复杂度)优先于代码可读性等更重要的指标是对这一重要准则的严重违反。
发布于 2016-07-28 19:38:16
如果您有一个大的方法,简单地将方法分解成更小的单元,除了调整度量之外,不会增加任何额外的好处。
复杂性可通过以下方式加以解决:
有时候重构是合适的。我们有一个复杂的方法,它做五件事,这是有意义的重构为一个主要的方法,调用5个子方法。这将使代码更容易理解和测试。我们可能可以消除一些代码行,但最终我们可能有相同数量的代码行,我们开始使用。乍一看,这些类型的工作看起来不错,可能会针对您的代码基础产生更好的度量标准,但总体上来说,所增加的价值可能比预期的要小。你必须确定这种分解是否合适。显然,将一个大方法分解为10s或10秒的子调用或嵌套调用可能会带来眼镜蛇效应,因为您的好意会使事情变得更糟。你想要避免这种情况。
有时,我们有一种方法太复杂,不能简单地进行旧的重构或分解成较小的块。在这种情况下,我们可能需要重新设计。重新设计可以引入新的设计模式(S)和/或简化代码的新类。因此,与1000行代码相比,重新设计的最终结果是200行代码。这些努力要困难得多,但从度量的角度和从整体代码的角度来看,它们提供了更多的价值。代码少,复杂少。
权衡总是时间和努力。有时,预算只能允许小规模的重构,如第一个示例所示。有时是复杂的大修,如第二种情况。
开发人员应该始终通过监管自己、提供同行评审和正式的代码评审来监视代码库的运行情况,并将代码添加到解决方案中。这将使复杂性保持在可管理的水平。
https://softwareengineering.stackexchange.com/questions/326047
复制相似问题