
代码用于创建接口。 但是代码本身是一个接口。
尽管代码的可读性非常重要,但是这个概念的定义很差-常常只是一组规则的形式:使用有意义的变量名,将大型函数分解为较小的函数,并使用标准设计模式。
同时,可以肯定的是,每个人都必须处理符合这些规则的代码,但由于某种原因,这是一团糟。
您可以尝试通过添加新规则来解决此问题:如果变量名变得很长,则需要重构主逻辑; 如果在一类中积累了许多辅助方法,也许应该将其分为两类; 设计模式不能在错误的上下文中应用。
这样的指令变成了主观决策的迷宫,要进行导航,您将需要一名能够做出正确选择的开发人员-也就是说,他必须已经能够编写可读的代码。
因此,指令集不是一种选择。 因此,我们将不得不对代码的可读性进行更广泛的描述。
为什么需要可读性
实际上,良好的可读性通常意味着代码易于阅读。 但是,对于这样的定义,我们不能走得太远:首先,它是主观的,其次,它使我们不得不阅读普通的文本。
不可读的代码被看作是一种伪装成代码的小说:很多评论揭示了正在发生的事情的本质,需要顺序阅读的文本片段,精巧的表述,其唯一含义是“智能”,害怕重复使用单词。 开发人员正在尝试使代码可读,但是针对的读者类型错误。
文本可读性和代码可读性不是一回事。
翻译成Alconost代码用于创建接口。 但是代码本身是一个接口。
如果代码看起来很漂亮,是否意味着它易于阅读? 美学是可读性的令人愉快的副作用,但是作为一个标准,它并不是很有用。 也许在极端情况下,项目中代码的美感将有助于留住员工-但是,即使成功了,您也可以提供良好的社交包。 此外,每个人对“美丽代码”的含义都有自己的看法。 随着时间的流逝,这种可读性的定义变成了关于表格,空格,方括号,“驼峰表示法”等的争论。虽然看到了错误的缩进,但任何人不太可能会失去意识,尽管这会在检查代码时引起注意。
如果代码产生的错误较少,是否可以认为它更具可读性? 错误越少越好,但是有什么机制? 在阅读代码时,我该如何赋予您模糊的愉悦感? 另外,无论在阅读代码时眉毛多么皱眉,这都不会增加错误。
如果代码易于编辑,是否可读? 但这也许是正确的思想方向。 需求变更,功能添加,错误产生-有时必须有人编辑您的代码。 为了不引起新的问题,开发人员需要了解他正在编辑的内容以及所做的更改将如何改变代码的行为。 因此,我们发现了一个新的启发式规则:可读代码应易于编辑。
哪个代码更容易编辑?
我立即想弄清楚:“当变量名有意义地给出时,代码更容易编辑,”但是我们只是将“可读性”重命名为“易于编辑”。 我们需要更深入的了解,而不是以不同的眼光看待同一套规则。
让我们先忘记一下我们在谈论代码。 已有数十年历史的编程只是人类历史规模上的一个问题。 将自己限制在这个“点”上,我们无法深入研究。
因此,让我们通过几乎在每个步骤中都遇到的设计界面的角度来查看可读性-不仅是数字界面。 玩具具有使其可以骑行或发出吱吱声的功能。 门具有允许您打开,关闭和锁定门的界面。 本书中的数据按页面收集,与滚动相比,提供了更快的随机访问。 在学习设计时,您可以了解有关这些接口的更多信息-请咨询设计团队。 在一般情况下,即使我们并不总是知道是什么使它们良好,我们都更喜欢良好的接口。
代码用于创建接口。 但是,代码本身与IDE结合在一起是一个接口。 专为极少数用户(我们的同事)设计的界面。 此外,我们将它们称为“用户”-为了保留在设计用户界面的空间。
考虑到这一点,请考虑以下用户路径示例:
- 用户想要添加新功能。 这需要找到正确的位置并添加功能,而不会产生新的错误。
- 用户想要修复该错误。 他将需要找到问题的根源并编辑代码,以使错误消失并且不会出现新的错误。
- 用户希望确保在临界情况下代码以某种方式运行。 他将需要查找特定的代码段,然后跟踪逻辑并模拟发生的情况。
依此类推:大多数路径遵循类似的模式。 为了不使事情复杂化,请考虑特定示例-但不要忘记这是对一般原则的搜索,而不是规则列表。
我们可以放心地假设用户将无法立即打开所需的代码部分。 这也适用于您自己的爱好项目:即使函数是由您编写的,也很容易忘记它的位置。 因此,代码应易于在其中找到合适的代码。
为了实现方便的搜索,您将需要对搜索引擎进行一些优化-在这里,有意义的变量名可以帮助我们。 如果用户找不到功能(从已知点沿调用堆栈移动),则可以按关键字开始搜索。 但是,名称中不能包含过多的关键字。 通过代码搜索时,将寻找唯一的入口点,从那里您可以继续进行进一步的工作。 因此,用户需要帮助到达特定的位置,并且如果您过度使用关键字,则会有太多无用的搜索结果。
如果用户能够立即验证在特定逻辑级别上一切都正确,则他可以忘记先前的抽象层,而将精力放到下一层。
您也可以使用自动完成功能进行搜索:如果您对要调用的函数或要使用的枚举有一个大致的了解,则可以开始输入所需的名称,然后从自动完成功能列表中选择适当的选项。 如果该功能仅用于某些情况,或者由于使用功能而需要仔细阅读其实现,则可以通过使用一个更真实的名称来表明这一点:通过滚动查看自动完成列表,用户宁可避免看起来复杂的事情-除非他确定是什么
因此,简短的常规名称更可能被视为默认选项,适用于“休闲”用户。 具有此类名称的函数应该不会感到惊讶:您不能将setter插入看起来像简单getter的函数中,其原因与界面中的“查看”按钮不应更改用户数据的原因相同。

在面向客户的界面中,熟悉的功能(例如暂停)几乎不需要文本。 随着功能变得越来越复杂,名称增加了,这使用户放慢了思考的速度。 截图-Pandora用户希望快速找到正确的信息。 在大多数情况下,编译需要花费大量时间,并且在运行的应用程序中,您将必须手动检查许多不同的边界情况。 如果可能,我们的用户将更喜欢阅读代码并理解其行为,而不是设置断点并运行代码。
若要在不运行代码的情况下进行操作,必须满足两个条件:
- 用户了解该代码正在尝试执行的操作。
- 用户确定代码可以执行其声明的操作。
抽象有助于满足第一个条件:用户应该能够深入到所需的详细程度的抽象层。 想象一下一个分层的用户界面:在第一级,导航是在广泛的部分进行的,然后越来越具体化-到需要更详细研究的逻辑级别。
文件或方法的顺序读取在线性时间内执行。 但是,如果用户可以上下移动调用堆栈-这是在树中进行的搜索,并且如果层次结构平衡良好,则此操作将在对数时间执行。 接口中肯定有用于列表的空间,但是您应该仔细考虑在某些情况下是否应该有两个或三个以上的方法调用。

在简短菜单中,分层导航要快得多。 在右侧的“长”菜单中-仅11行。 在方法代码中,我们多久一次适合这个数字? 截图-Pandora不同的用户对于第二种情况有不同的策略。 在低风险情况下,注释或方法名称是充分的证据。 在风险更高,更复杂的区域,以及当代码不相关注释过载时,后者可能会被忽略。 有时,甚至方法和变量的名称也会引起疑问。 在这种情况下,用户必须阅读更多代码并记住更广泛的逻辑模型。 将上下文限制在易于保留的小区域也将有所帮助。 如果用户能够立即验证在特定逻辑级别上一切都正确,则他可以忘记先前的抽象层,而将精力放到下一层。
在这种操作模式下,各个令牌开始变得越来越重要。 例如,一个布尔标志
element.visible = true/false
与其他代码隔离起来很容易理解,但这需要将两个不同的标记结合在一起。 如果使用
element.visibility = .visible/.hidden
这样就可以立即了解该标志的值:在这种情况下,您无需读取变量名即可发现它与可见性有关,我们在设计面向客户的接口时也看到了类似的方法。 在过去的几十年中,“确定”和“取消”按钮已变成更具描述性的界面元素:“保存并取消”,“发送并继续编辑”等,以了解将要执行的操作,用户只需阅读建议的选项,而无需阅读整个上下文就足够了。
上例中的“脱机模式”行指示该应用程序处于脱机状态。 下面的示例中的开关具有相同的含义,但是要理解它,您需要查看上下文。 截图-Pandora单元测试还有助于确认代码的预期行为:它们充当注释-但是,由于它们更相关,因此可以在更大程度上受到信任。 是的,他们还需要完成组装。 但是在使用完善的CI管道的情况下,测试会定期运行,因此在更改现有代码时可以跳过此步骤。
从理论上讲,安全性来自于充分的了解:只要我们的用户了解了代码的行为,便可以安全地进行更改。 在实践中,您必须考虑开发人员是普通人:我们的大脑使用相同的技巧并且很懒。 因此,您花费在理解代码上的精力越少,我们的操作就越安全。
可读的代码应将大多数错误检查传递给计算机。 一种方法是使用“断言”调试检查,但是,它们也需要组装和启动。 更糟糕的是,如果用户忘记了临界情况,则断言将无济于事。 用于检查经常被遗忘的边界案例的单元测试可以做得更好,但是一旦用户进行了更改,您将不得不等待测试运行。
总结:可读代码应易于使用。 并且-作为副作用-它可能看起来很漂亮。
为了加快开发周期,我们使用了编译器中内置的错误检查功能。 通常,在这种情况下,不需要完整的装配,并且实时显示错误。 如何利用这个机会? 一般来说,您需要找到编译器检查变得非常严格的情况。 例如,大多数编译器没有全面研究“ if”语句的描述,而是仔细检查“ switch”以查找遗漏的情况。 如果用户尝试添加或更改条件,则如果以前所有类似的运算符都全面,它将更加安全。 并且,当“大小写”条件发生变化时,编译器将标记所有需要检查的其他条件。
另一个常见的可读性问题是在条件表达式中使用原语。 当应用程序解析JSON时,此问题尤其严重,因为您只想在字符串或整数等式周围添加“ if”语句。 这不仅增加了输入错误的可能性,而且使用户确定可能值的任务变得复杂。 在检查边界情况时,何时可能出现任何行与何时-仅两个或三个单独的选项之间存在很大差异。 即使将原语固定为常量,您也应该赶紧一次,尝试按时完成项目,然后会出现一个任意值。 但是,如果您使用专门创建的对象或枚举,则编译器将阻止无效的参数并提供有效参数的特定列表。
同样,如果不允许布尔标志的某些组合,请用单个枚举替换它们。 以一个可能处于以下状态的构图为例:缓冲,完全加载和播放。 如果您将加载和播放状态想象成两个布尔标志
(loaded, playing)
编译器将允许输入无效值
(loaded: false, playing: true)
如果您使用枚举
(.buffering/.loaded/.playing)
那么就不可能指示无效状态。 在面向客户的界面中,默认设置应为禁止无效的设置组合。 但是,当我们在应用程序内部编写代码时,我们常常忘记为自己提供相同的保护。

无效的组合会被预先禁用; 用户无需考虑哪些配置不兼容。 屏幕截图-苹果按照所考虑的用户路径,我们得出了与开始时相同的规则。 但是,现在我们有了一个原则,可以根据这些原则独立制定和更改它们。 为此,我们问自己:
- 用户搜索所需的代码是否容易? 搜索结果中是否会包含与查询无关的功能?
- 找到所需代码的用户可以快速检查其行为的正确性吗?
- 开发环境是否提供安全的编辑和代码重用?
总结:可读代码应易于使用。 并且-作为副作用-它可能看起来很漂亮。
注意事项
- 布尔变量似乎更便于重用,但是此重用选项暗示了互换性。 以tapable和cached标志为例,它们代表位于完全不同的平面上的概念:单击元素的能力和缓存状态。 但是,如果两个标志都是布尔值,则您可能会意外地交换它们,从而在一行代码中得到一个非平凡的表达式,这意味着缓存与单击元素的能力有关。 在使用枚举时,为了形成这种关系,我们将被迫创建一个明确的,可验证的逻辑,以转换我们使用的“度量单位”。
关于翻译这篇文章由Alconost翻译。
Alconost以70种语言
本地化游戏 ,
应用程序和网站 。 母语翻译,语言测试,带有API的云平台,持续本地化,24/7项目经理,任何格式的字符串资源。
我们还制作
广告和培训视频 -用于销售,图像,广告,培训,预告片,专家,Google Play和App Store的预告片的网站。
→
了解更多