您可能会讨厌这个或一个关于代码看起来应该如何的故事

为了寻找完美的代码,已经破坏了多少副本,并且仍然会破坏它们。显然时间到了,我应该参加:)


祝大家有美好的一天。 不久前,我与学生讨论了“我们期望从好的代码中得到什么”这一主题,并决定在此处重复。 在翻译过程中,文本有所变化,但本质保持不变。 这篇文章看起来很简单(当然也不完整),但是这里有一个合理的解释。


代码应该工作


显而易见的事情并不会因它们显而易见而带来更少的问题。许多项目崩溃只是因为开发完全无法解决实际的用户问题


让我们谈谈我们对代码的期望。 好吧,对于初学者来说,它应该可以工作。


当然,这听起来很明显,但是我们每个人都曾经尝试或成功启动了甚至都不会使用的代码,所以请不要笑。 第二点-代码应在不正确的情况下正常工作。 那是捕捉错误。 但是,让我们回到第一点,再谈一点。


周期性地,我收到一个我不知道该怎么做的任务。 也就是说,一般而言(我尝试环顾四周,并不断尝试一些新事物)。 在这里,我立即被吸引去写一些抽象,某种基础架构,以延迟实际工作的时间。 所以,这是错误的。 该代码应该工作。 我知道我在重复自己,但这是重要的一点。 如果您不知道如何解决问题,请不要着急创建接口,模块,仅此而已。 这是一个坏主意,它将在您的时间尽头结束,并且您将无处可去。 请记住,工作不佳的代码比好代码好很多倍,但不能工作。


关于生产相同产品的两家软件公司,有一个古老的寓言。 无论如何,第一个做的很好,但是第一个进入了市场,第二个做的很完美,很晚了。 结果,第一个战役设法占领了市场,并购买了第二个公司。 这是关于另一个的,但是主要思想仍然是相同的。 首先我们解决问题,然后使代码漂亮。


通常,首先制作一个可以工作的原型。 让它变得la脚,歪曲和不快乐,但是当被问到时,您可以说解决方案已经存在,仍然需要集成它。 并重写它应该。 您可以尝试用这样的格言来表达这一点-如果您知道如何完成任务,请做好。 如果您不知道,请先以某种方式解决。


还有一点很重要。 我希望你能理解。 这不是写错误代码的要求。 代码应该不错。 这是对First Thing First的调用-首先代码起作用,然后重构。


现在让我们来谈谈屎屎发生。 因此,我们有了代码,它甚至可以工作。 相反,它“有效”。 让我们看一个简单的例子:


public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } } 

这是“工作”代码的一个很好的例子。 怎么了 因为没有早晚考虑,我们的端点将掉线。 此示例未考虑所谓的边缘情况-边界线,“不良情况”。 当您开始编写代码时,请考虑可能出什么问题。 实际上,我不仅在谈论远程调用,还在谈论您无法控制的所有资源-用户输入,文件,网络连接,甚至数据库。 所有可能中断的事件都会在最不适当的时刻中断,并且您唯一可以做的就是尽可能地为此做好准备。


不幸的是,并非所有问题都如此明显。 几乎可以保证一定数量的问题区域会产生错误。 例如,使用区域设置和时区。 痛苦和尖叫“一切都在我的机器上进行”。 他们只需要仔细了解并与他们合作即可。


关于用户输入。 有一个很好的原则,那就是除非经过证实,否则任何用户输入都被认为是不正确的。 换句话说,请始终验证用户输入的内容。 是的,在服务器上也是如此。


总计:


  • 首先使代码起作用,
  • 然后让他好
  • 不要忘记边缘情况和错误处理。

现在让我们谈谈代码支持


支持是一个复杂的概念,但是在这里我将包括三个组件-代码应易于阅读,易于更改且保持一致。


谁用俄语写评论? 没有人在写作吗? 太好了 通常,问题之一是非英语代码。 不要那样做 我用挪威语的类编写了一段代码,但我无法说出它们的名字。 真伤心 显然,支持这样的代码(对于非挪威人)将不是一件容易的事。 但这很少见。


通常,易读性与命名和结构有关。 实体的名称-类,方法,变量,应简单,易读并具有含义。 拿我们前面的例子。


 public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } } 

尽管实现了,您能理解Do方法做什么吗? 几乎没有 与变量名类似。 为了了解哪种xx对象,您需要查找其声明。 这花费了我们的时间,使我们无法理解代码中发生的一般情况。 因此,名称必须反映动作或含义的实质。 例如,如果将Do方法重命名为GetUserName,代码将变得更加清晰,在某些情况下,我们不再需要查看其实现。 类似地,变量名的格式为x和xx。 确实,普遍接受的例外形式是e表示错误,i表示k,循环计数器,n表示尺寸,等等。


再举一个例子,以您一个月前编写的代码为例,并尝试流畅地阅读它。 你知道那里发生了什么吗? 如果是这样,我祝贺你。 如果不是,则您对代码的可读性有疑问。


总的来说,有这样一个有趣的报价:


“计算机科学中只有两件难事:缓存失效和命名。” ©Phil Karlton

在计算机科学中只有两件复杂的事情:缓存失效和命名。


给实体命名时,请记住她。


可读代码的第二个组成部分是其复杂性或结构。 我说的是那些喜欢编写六个嵌套ifas或将回调写到该回调内部的回调中的人。 JavaScript甚至有一个称为Callback Hell的术语。


谈论完美的代码很容易,但是编写起来却有点困难。这里最重要的是至少不要对自己撒谎。如果您的代码不正确,请不要将其称为“ candy”,而要把它完成


怜悯你的同事和你自己。 一周后,您将不得不逐字浏览此代码以修复或添加某些内容。 避免这种情况并不是那么困难:


  • 编写短函数,
  • 避免大量分支或嵌套,
  • 即使您不打算重用逻辑代码块,也要将其分成单独的函数,
  • 使用多态性代替if

现在让我们谈谈更复杂的事情-好的代码易于更改。 谁知道“大泥巴”这个词? 如果有人不熟悉-请看图片。


总的来说,在这方面,我真的很喜欢开源。当您的代码向全世界开放时,我想以某种方式使其至少正常。


每个模块都取决于每个模块,合同中的某个地方的更改很可能导致极地狐的出现,或者至少会导致很长时间的调试。 从理论上讲,这很简单-减少代码对自己代码的依赖。 您的代码对任何实现细节了解得越少,它就会越好。 但是实际上,它要复杂得多,并导致代码重新复杂化。


以提示的形式,我可以这样说:


  • 尽可能隐藏您的代码。 想象一下,明天您将必须手动将其从项目中删除。 您将要修理多少个地方,这将有多困难? 尝试最小化此数量。
  • 避免循环依赖。 将代码分成几层(逻辑,接口,数据访问),并确保“较低”级别的层不依赖于“较高”级别的层。 例如,对数据的访问不应依赖于用户界面。
  • 将功能分组到模块(项目,文件夹)中,并在其中隐藏类,仅保留外观和界面。

和画。 只需画一张纸,应用程序将如何处理您的数据以及为此使用了哪些类。 这将帮助您在一切都变得无法弥补之前了解过于复杂的地方。


最后是均匀性。 即使您觉得不合适,也要始终坚持团队采用的制服风格。 这适用于格式化以及解决问题的方法。 即使更快,也不要使用~~进行舍入。 团队将不胜感激。 在开始编写新代码时,请始终查看项目,也许您已经实现了所需的东西,并且可以重用它。


总计:


  • 正确命名
  • 良好的结构
  • 均匀性

该代码应该是高效的


让我们有点感冒。 我们将考虑的下一个要求是代码应具有很高的生产力。


“足够”一词是什么意思? 可能每个人都听说过早的优化是有害的,它们会破坏可读性并使代码复杂化。 没错 但是,您也应该知道自己的工具,而不要写在工具上,以便网络邮件客户端将Core I7加载60%。 您应该了解导致性能问题的典型问题,即使在编写代码阶段也要避免这些问题。


让我们回到我们的例子:


 public string GetUserName(int userId) { using (WebClient http = new WebClient()) { return http.DownloadString("https://some.super.url"); } } 

此代码有一个问题-通过网络同步下载。 这是一个I / O操作,它冻结我们的流程直到执行它。 在桌面应用程序中,这将导致接口悬空,而在服务器应用程序中,这将导致无用的内存保留以及对服务器的请求数量用尽。 只要知道了这些问题,您就可以编写更多优化的代码。 在大多数情况下,这就足够了。


但是有时候,不会。 因此,在编写代码之前,您需要事先了解针对性能设置了哪些要求。


现在让我们谈谈测试。


这与上一个主题同样重要,甚至可能更多。 一切都因测试而复杂。 让我们从下面的语句开始-我认为该代码应包含相当数量的测试。


为什么我们甚至需要代码覆盖率和测试? 在理想的世界中,不需要它们。 在理想的世界中,代码编写时没有错误,并且需求永不改变。 但是我们生活在远离理想世界的环境中,因此我们需要进行测试以确保代码正确运行(没有错误),并且在更改某些内容后代码能够正确运行。 这就是测试带给我们的好处。 另一方面,即使测试涵盖了100%(由于计算指标的具体细节)也不能保证您涵盖了所有内容。 此外,每项附加测试都会减慢开发速度,因为更改功能后,您还必须更新测试。 因此,测试的数量应该是合理的,并且主要的困难在于找到代码量和系统稳定性之间的折衷方案。 找到这个方面非常困难,并且没有通用的方法可以做到这一点。 但是,有些技巧可以帮助您做到这一点。


  • 涵盖业务应用程序逻辑。 业务逻辑是为应用程序创建的全部内容,并且应尽可能稳定。
  • 涵盖复杂的,有计划的事情。 计算,转换,复杂数据合并。 一个容易出错的地方。
  • 掩盖错误。 错误是一个标志,告诉我们这里的代码易受攻击。 这是在此处编写测试的好地方。
  • 涵盖经常重复使用的代码。 它很有可能会经常更新,我们需要确保添加一件事不会破坏另一件事。

不需要太多就不要覆盖


  • 外部库-查找其代码已被测试覆盖的库。
  • 基础结构-DI,自动映射(如果没有复杂的映射)等等。 有e2e或集成测试。
  • 琐碎的事情-将数据分配给字段,转发呼叫等。 几乎可以肯定,您会找到更多有用的地方进行测试。

好吧,基本上就是这样。


总结一下。 好的代码是


  • 工作代码
  • 易读
  • 容易改变
  • 足够快
  • 并以适当的数量包含在测试中。

以这种困难的方式祝你好运。 而且很可能您会讨厌这一点。 好吧,如果还没有,欢迎您。


实际上,这是一个发现,机会和创造力的奇妙世界。只有在附近,这里才出现形式打扰的乏味,20余年的遗留代码黯淡无光,a不休的历史。因此,我宁愿将其称为发际桥(C)。寻找您将要刻录的项目。尝试做点好事。简单地说,使世界变得更美好,一切都会好起来的。

Source: https://habr.com/ru/post/zh-CN429732/


All Articles