上周我收到了一个问题,我回答得非常简短,但我觉得要完整地回答它需要一篇博文。所以这是博客文章。
一段代码的作者是否应该不仅仅负责单元测试,还是同行评审有用?
加载的问题,对不对?显然,问这个问题的人认为作者应该进行单元测试和同行评审。并且我同意。但是将程序员的角色限制在这两件事上是一种非常狭隘的编程观点。
然后我开始考虑它并意识到我们试图通过代码审查完成的大部分工作都包含了我们想要包含在开发人员负责的事情列表中的所有事情。此外,代码审查主要是确保 1) 代码完成其预期的工作,以及 2) 代码在未来易于维护的审查。
换句话说,“同行评审”或“代码评审”一词所包含的一切主要是关于代码清晰度。
代码清晰度解决了我们在选择一段新代码时都会遇到的问题。你能轻松理解吗?我刚刚听了 DotNetRocks 的一集 ,讲的是我们大部分时间都花在阅读代码上,但没人在谈论我们如何才能做得更好。我们可以更好地阅读代码的一种方法是首先将代码写得更清楚。
这里有 15 种方法可以让你的代码更易于阅读:
让你的代码漂亮
我记得当我在做 Clipper 编程时,我们有一个人总是展示可以在教科书中印刷的代码。我的意思是,无论您对他实际编写的代码有何评论,代码的视觉呈现都是一件美事。他让我们相信他所有的代码看起来都是那样。然后他去了其他公司工作,我们实际上看了他写的其他代码。它看起来就像任何其他代码。
但它给我上了一课。不幸的是,我仍然需要提醒一个教训。漂亮的代码更容易维护,并且在很大程度上暗示了代码的质量。就像你不穿西装就不会去面试一样,因为你的着装暗示了你将如何编程,你的代码应该在你开始阅读之前通过它的外观来暗示质量。
为您的代码建立并遵守命名约定
我以前写过这个。最值得注意的是 .NET 在有关 匈牙利表示法 的文章中首次出现。正如我在那篇文章中指出的那样,不使用匈牙利表示法并不意味着您没有任何标准。当然,您不想在所有整数前加上小写字母“I”,因为您不关心它是什么类型的数字。但是在按钮前加上“button”是很有价值的,这样在你的 Intellisense 中更容易找到它,或者如果你需要搜索你的代码以查找你命名的按钮,那么在你的代码中找到所有按钮要容易得多这边走。
但是建立和遵守命名约定还有另一个原因。完成此操作后,使用这些通用约定的每个人都可以更轻松地阅读您的代码。
例如,在我工作的地方,我们有一个我认为非常奇怪的命名约定。我们所有的 POCO 类(我们自己生成这个,因为我们使用的是 DB2)都是 UPPERCASE_UNDERSCORE_SEPARATED。我们所有的 POCO 集合属性都被命名为 UPPERCASE_UNDERSCORE_SEPARATEDs。它曾经让我发疯,主要是因为我虔诚地使用 ReSharper ,并且没有设置默认规则来处理这种奇怪的约定。但是,这是一个标准,我现在可以告诉你,你打开一个使用 POCO 的文件,你知道你正在处理 POCO。毫无疑问!
虽然我仍然不推荐这种做法,但我告诉你这个例子是因为它说明了编码标准有多少可以在你开始阅读代码之前立即告诉你一些关于你正在处理的代码的信息。
建立并遵守通用架构
我在阅读别人的代码时遇到的最大问题之一是,如果我无法理解他们为什么把东西放在他们放的地方。但是拥有一个易于解释的通用架构有助于缓解很多这个问题。我喜欢使用框架的原因之一是因为很多时候这些决定已经为你做出了。例如,仅仅拥有一个简单的三层架构,将视图、业务规则和数据访问分离到代码的不同部分,就可以大大提高代码的可读性。在这个基本框架内,您可以在 Model View Presenter、Model View Controller 或任何其他设计模式中分层。遵循“万物皆有其位”的格言,您的代码将更容易遵循。
将对象命名为名词,将方法命名为动词
这是非常基础的,我几乎觉得我应该在这里写下它。但我仍然看到原则一直被违反。所以,就在这里。显然,如果您不遵循面向对象的方法。假设您正在进行函数式或过程式编程,则需要根据您的环境调整此规则。但是对于 OOP 男孩和女孩来说。停止违反此规则。这是不成熟的表现。
命名变量它们是什么
类似地,停止使用短变量名,除非您使用的语言仍然过时以至于需要它。我以前写过这个。实际上两次。 关于使用 i、j 和 k 作为变量名的咆哮 。这里的要点是,您希望能够阅读您的代码,并且要做到这一点,您的代码必须告诉您一些关于您正在做的事情。短变量名几乎总是无助于代码的可读性。
不要在你的方法中包含名词
这不同于仅仅说方法应该是动词。我在很多地方都看到过使用动词和名词作为方法名称的代码。如果您的方法中既有名词又有动词,那么您的类可能尝试做的事情太多了。
如果您有一个 Person 类,其中有一个名为 FirstNameToLower() 的方法,这看起来会不会很有趣?
为你的方法建立一个圈 复杂度 阈值并虔诚地遵守它们
圈复杂度考虑了必须执行多少语句,最重要的是,代码中必须执行多少条件。减少圈复杂度的最简单方法是减少条件的数量。但除此之外,您可以通过尽可能消除嵌套来使您的代码更具可读性。
我的最爱之一是不编写如下所示的代码:
void SomeMethod()
{
if(x != y)
{
// do stuff here
}
}
您可以通过这样编写代码来提高代码的可读性:
void SomeMethod()
{
if(x != y)
{
// do stuff here
}
}
类似地,与其在不起作用时嵌套,如包含其他 while 语句的 while 语句,不如为每个 while 语句创建一个方法?
使代码“自我记录”
也就是说,如果您觉得您的代码需要注释,您可能违反了其他可读性建议中的一项或多项。这并不是说您永远不应该添加注释,但它们不应该替代可读代码。我通常对我为什么做某事留下评论,而将我做了什么留给代码。
不要为您可以通过阅读代码推断出的事情添加注释
“下一行将 loopVar 递增 1”对任何人都没有任何好处,最终可能会成为代码中的垃圾,使代码更难阅读。为什么?因为 loopVar 有可能被重命名或删除,并且注释可能会永远存在。在我们获得能够验证我们的注释和可执行代码的编译器之前,在注释中引用变量通常是一种应该避免的不良做法。
代码 可靠 吗?
- 类的单一职责。
- 打开/关闭原则。
- 里氏替换原则。
- 接口隔离原则。
- 依赖倒置。
代码可测试吗?
如果您遵循以上所有规则,代码应该很容易测试。但是,这是您应该明确审查您的代码的事情。
代码会失败吗?
任何人都可以编写使用任何特定方法的代码,这些代码会使方法崩溃、抛出异常或以其他方式做一些意想不到的事情吗?
代码重复
我不了解你,但我很确定我的代码库中到处都是看起来几乎相同的代码。我当前的目标之一是,每当我准备复制和粘贴超过三行的代码时,让自己创建一个新方法。
100% 代码覆盖率
假设你有单元测试,你是否对圈复杂度分数大于 1 的所有方法都有 100% 的代码覆盖率?我说的是你写的代码。我之前写过 什么代码应该测试 ,什么代码不需要测试。
学习您的语言的词汇
John Sonmez 在 2013 年写了一篇非常引人注目的文章,内容是关于 是什么让代码具有可读性 。他在其中说,就像我们学习阅读时一样。高级开发人员可读的代码可能无法被入门级开发人员阅读,原因很简单,因为入门级开发人员没有牢牢掌握语言。像 John 一样,我因使用三元运算符而受到批评,因为它太简洁了。描述不够。不可读。我总是回应说,争论语法不清晰就像争论一个句子不清晰,因为它使用了一个你不熟悉的词。这个词实际上可能是作者试图传达的完美词。你对那个词的理解不会使写作变得糟糕。这只是意味着读者还有更多的学习要做。
您还发现什么使您的代码更具可读性?在下面给我留言。