如何定义和衡量代码的简单性?


13

在上一个有关与可读性相关的简单性的问题中,有很多答案可以帮助我看到我对代码简单性的定义和理解是不正确的。

如何定义代码的简单性?哪些软件度量和指标可用于度量代码的简单性?


2
@MarkTrapp还有其他讨论代码简单性的方法,而没有来自经验软件工程的主题,这些主题我都不那么熟悉。例如,就编写自动化测试的能力来讨论简单性。我的技能和知识使我可以从经验丰富的软件工程师的角度回答这个问题,而其他人则可以从其他角度回答。在问题中添加该陈述会极大地限制有用答案的数量,从而使其过于局限。如果要添加它,可以,但这是一个很好的问题。
Thomas Owens

2
@ThomasOwens 真正的问题是答案,而不是想法或意见。缩小范围,以便每个人都以相同的方式解释如何回答问题,这正是Stack Exchange的含义。解决问题的方法可能不止一种,但只有一个明确说明的问题。

在目前的状态下,该问题的答案很少(我的答案针对的是经验软件工程的观点,具有共同的指标-可能还有其他指标)。从其他角度排除提供有效替代方案的答案是没有意义的,这就是该问题的措辞。我完全不同意这些修改,应该将问题恢复为原始形式。
Thomas Owens

@MarkTrapp问题很明确:如何确定代码的简单性?有几个好的答案。我的是使用经验软件工程技术来衡量复杂性。另一个可能是编写自动化测试,如果很难编写好的测试,则代码很复杂-一个完全有效的答案。可能还有其他我不知道的地方。如果您需要衡量代码库的复杂性/简洁性,则应以允许所有替代方案均能呈现的方式措辞该问题,以便提问者可以针对其特定情况选择最佳解决方案。
Thomas Owens

Answers:


16

衡量复杂性(或简单性,如果您认为简单复杂性相反)的最常见指标是McCabe的Cyclomatic ComplexityHalstead复杂性指标

循环复杂度衡量通过给定单元(通常是方法或函数)的不同路径的数量,尽管也可以在类上进行计算。随着路径数量的增加,记住通过给定模块的数据流变得更加困难,这与工作内存的概念有关。较高的圈复杂度往往表明测试模块的能力很困难-需要更多的测试用例才能涵盖通过系统的各种路径。也有研究将高复杂度与高缺陷率联系在一起。通常,圈复杂度为10表示应检查某个单元并可能对其进行重构。

Halstead复杂度度量使用总计和不同的运算符和操作数的输入来计算一段代码的数量,难度和工作量。难度((唯一运算符的数量/ 2)*(操作数的总数/唯一操作数的数量)与读取和理解代码(例如学习系统或执行代码检查)的能力有关。同样,您可以在系统级别,类级别或方法/函数级别进行计数。这里这里都有一些关于计算这些度量的信息

只需对代码行进行计数也可以使您了解复杂性。更多的代码行意味着在模块中有更多的阅读和理解。我不愿意将其用作独立测量。相反,我将其与其他度量结合使用,例如给定模块中的缺陷数量以获取缺陷密度。较高的缺陷密度可能表示编写测试和执行代码检查时出现问题,这些问题可能是也可能不是由复杂的代码引起的。

扇入和扇出是另外两个与数据流相关的指标。作为定义在这里,风扇是的程序调用,参数读取的总和,和全局变量读取和扇出是调用一个给定的程序程序的总和,写入参数(暴露给外部用户,在按引用传递)和写入的全局变量。同样,高扇入和扇出可能表示可能难以理解的模块。

在特定的范例中,可能还有其他有用的度量或指标。例如,在面向对象的世界中,可以使用监视耦合(期望低),内聚性(期望高)和继承深度(期望低)来评估系统的简单程度。

当然,重要的是要意识到很多度量和指标只是指标。您需要使用判断力来确定是否有必要进行重构以提高简单性,或者这样做是否值得。您可以进行测量,计算指标并了解代码,但是您不想根据数字来设计系统。最终,做有意义的事情。


5
我知道您提到过它,但必须强调,圈复杂度实际上仅在功能/方法级别有用,而在较高级别上变得更加主观/无用。
Ryathal 2011年

麻烦的是,尽管这些措施是很好的一般指导。在许多情况下,“不良”程序的得分很高,例如,具有十几个函数,而具有两个以上参数的单个函数就足够了,相反,许多写得很好的复杂问题解决方案的“良好”程序也可以得分不好
詹姆斯·安德森

@James我明确指出了这一点。任何度量或度量都需要根据上下文进行衡量,以作为应注意的事项的指标。需要工程师的判断来确定是否需要采取纠正措施以及采取何种措施。但是,除非您正在积极地收集数据,否则没有经验方法可以了解潜在的问题。
Thomas Owens

7

我不想看定义简单性的正式模式,而是想将简单性定义为代码编写质量的一个属性。

我不是在说简单性,而是在什么时候称呼简单与否。

1.代码遍历:
浏览代码有多容易?是否容易发现API函数的编写位置?是否容易理解调用流程,例如哪些方法正在调用其他方法(以及为什么)-是否实施了良好的状态机或明确标识的算法?

当代码遍历很容易时,代码很容易遵循。

2.命名
尽管其他编码标准有助于使代码看起来更简洁-最重要的是类/对象实例/变量/方法的命名。在使用清晰明确的名称显然是对很大的影响简单的代码。如果很难识别一个简单的名称,则表明您可能需要重新考虑该变量/方法的想法。

3.解释和引用
您的每个方法是否都有明确的作用。每个变量/属性是否易于确定它们所扮演的角色?当一段代码执行隐含假设或影响无关变量集的操作时,可能会成为维护的噩梦。

4.依赖或耦合
这很难仅通过查看代码来判断,但是如果有人试图修复您的错误,这一点就很明显。当其他对象中的某些其他事物发生变化时,此处的操作是否发生变化?这些变化明显吗?您是否需要经常更改API来容纳东西。这些表明模块间的关系并不简单

5.输入用户或应用程序
最后,在API / UI上接受用户输入或应用程序有多简单?当多个可能的用户/应用程序(出于不同目的)需要给您时-它们明显吗?是否存在与较高抽象无关的状态/细节,但仍然从接口向前/向后前进?

我通常会问一个简单的问题:如果不是程序,而是要求人类执行相同的功能,我是否会将这些信息填写在纸上?如果没有,我在这里还不够简单

我不会说这个列表是详尽无遗的,但是我想我的标准是使用和修改软件有多容易或有多困难。那很简单。


1
他要求提供度量和指标。这些都是主观的,所以我认为它们不是很正确。
psr

@psr我同意你的看法。从托马斯的回答中也很明显。但是,他提到了与可读性有关的简单性。Thomas的答案涉及循环复杂性-这告诉您测试代码有多复杂,而不是从可读性上讲代码有多复杂,并可能扩展可维护性。这是两个截然不同的概念。这就是为什么我写了这个答案来摆出鲜明的矛盾的原因。不幸的是,据我所知,没有任何度量标准在可读性方面指代代码的简单性
Dipan Mehta,

“使用不可能被误解的名称” -恕我直言,这样做的目标太高了,以实现一个不切实际和不可能的目标。我宁愿不要这么确定,而只是说“使用清晰明确的名称”。
彼得Török

@PéterTörök我同意。我认为通常在许多组织中,对命名约定的定义非常明确,并且仍然对特定变量的含义感到困惑。因此强调说,目的明确意味着简单,而不是正式规则。可能是我对我的描述太过分了。谢谢。
Dipan Mehta

@Dipan通过工作内存,循环复杂性与代码的可读性有关。具有高循环复杂度(甚至只是高块深度)的代码很难保存在工作内存中,因此更难以直接阅读。
Thomas Owens

0

我不了解任何现有的简单代码度量标准(这并不意味着它们不存在-只是我不知道它们)。我可以提出一些建议,也许会有帮助:

  • 使用的语言功能的简单性:如果语言具有可以被视为“高级”和“简单”的功能,则可以计算高级功能的出现次数。您如何定义“高级”可能会更加主观。我想有些人可能会说这就像衡量程序的“聪明度”一样。一个常见的例子:有些人可能说?:操作员应该是“高级”功能,而另一些人可能会不同意。我不知道编写一个可以对此进行测试的工具有多么容易。

  • 程序中结构的简单性:您可以测量函数将接受的参数数量。如果你有> ň与>所有功能%的参数,你可以选择就当没有简单的,这取决于你如何定义ñ(也许N = 3和M = 6?)。我认为有一些静态分析工具可以测量此结果-我认为JTest可以简单地测量具有> m个参数的函数。

  • 您可以尝试计算嵌套循环或控件结构的数量。我认为这实际上不是一个坏指标,并且我想它有个名字(我想不起来了)。再次,我认为有些工具(再次类似于JTest)可以在一定程度上衡量这一点。

  • 您可以尝试测量“重构性”。如果你的代码中包含大量的代码块可以进行重构,但都没有,也许这将可能不会简单。我还记得从我与JTest一起工作时起,它也尝试测量这一点,但是我记得在这种情况下我并不经常同意它,所以YMMV。

  • 您可以尝试测量系统不同部分之间的层数。例如:在将Web表单中的数据存储到数据库之前,将触摸多少不同的代码段?要正确衡量这可能是一个棘手的问题...


2
我相信#3被称为区块深度。如果涉及决策控制结构,它也与循环复杂度有关。
Thomas Owens

没有下注的解释吗?
FrustratedWithFormsDesigner

不能同意“语言功能的简单性”。有高级功能可以简化 代码。仅使用简单的基本功能会掩盖代码的实际作用,这不可避免地导致泄漏抽象层。高级语言功能允许表达更高级别的抽象,从而使您的代码更加密集和可读。您正在使用的高级功能(当然是明智的选择)越简单越好。只需将Matlab(实际上是“高级”)中的代码与仅由基本功能组成的类似Fortran代码进行比较。
SK-logic

我也不会同意许多层指标。如果您可以通过许多琐碎而干净的步骤或一次扭曲的转换来完成某项操作,则最好在多个步骤中进行。许多简单且明显分开的层比单个扭曲层要好得多(也更简单)。
SK-logic

@ SK-logic:我想我应该把那个叫做“聪明”,它更接近我的意思。我只想说?:当它们嵌套5深时,它们就是一个问题。至于层,干净分离的层要好于一个回旋层。但是7个大部分为冗余的层(当只需要2或3个层时)是一件坏事。
FrustratedWithFormsDesigner
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.