Go语言编程之道 — Bitfield


“道”指事物的内在本质或自然倾向。例如,水趋向于向下流动:这就是它的道。你可以堵住它、引导它、泵送它或以其他方式干扰它,但尽管你付出了所有的努力,它最终还是会到达它要去的地方。
“道”,作为一种与世界接触的方式,就是对事物的自然倾向敏感,而不是浪费精力与它们斗争,而是与它们同行:一点点稍微与之一起共存,而不是与它作对.
以水作比喻,一个可怜的游泳者在水中翻来覆去,制造了很多噪音和大惊小怪,但几乎没有实际进展。另一方面,“道士”会冲浪。
那么什么是Go之道呢?如果我们以一种敏感、智能的方式在 Go语言中进行软件开发,遵循语言和问题的自然轮廓,而不是试图将它们推开,那会是什么样子?让我们尝试建立一些一般原则。
 

这是我的三件宝,持而保之;一是善良;第二,简单;第三,谦虚。——《道德经》
(原文:我有三宝,持而保之:一曰慈,二曰俭,三曰不敢为天下先。)
以仁慈和同理心编写程序是什么意思?
这意味着我们为人类编写代码,而不是为计算机编写代码。这些人容易犯错、缺乏耐心、缺乏经验、注意力不集中,而且在其他方​​面都是不完美的。通过对Go 代码的设计和细节进行一些思考,我们可以使他们的生活(和工作)变得更加轻松。
我们可以通过为我们的库提供描述性名称、使它们易于导入、良好记录它们以及为他们分配慷慨的开源许可证来对我们的用户友好。我们可以设计深度抽象,让用户利用小而简单的 API 来访问强大、有用的行为。
我们可以通过使程序易于安装和更新、要求最少的配置和依赖项、捕获最常见的使用错误和运行时错误并为用户提供有用、准确和友好的信息来善待那些运行我们程序的人关于出了什么问题以及如何解决。

聪明是一种恩赐。善良是一种选择。——杰夫·贝索斯

我们可以通过尽可能清晰、简单和明确来善待那些必须阅读我们的代码的人。我们可以给类型和函数赋予有意义的名称,在上下文中有意义,并让用户使用我们的抽象以直接、逻辑的组合创建他们自己的程序。我们可以通过坚持约定、实现标准接口和以显而易见的方式做显而易见的事情来消除认知障碍和降低难度。
在代码审查中,我们是温和而鼓励的。我们会在别人的工作中找到一些值得称赞的地方,我们不会将人们视为犯错误或忽略细节的愚蠢人。如果我们对自己诚实,我们就会承认我们犯了同样的错误。我们也知道接受批评对我们来说是痛苦和困难的,我们利用这些知识来帮助我们善意地缓和自己的批评。
最后,我们可以善待自己,编写出色的测试,让我们在未来更容易理解、修复和改进我们的程序,并且在发现错误或设计缺陷时不要对自己生气。

系统非常非常快地进入意大利面混沌状态。——约翰·奥斯特豪特

另一种善待我们未来的自己和我们的继任者的方法是:对程序的架构和整体设计进行小的、持续的改进。好的程序可以存活很长时间(也有一些是坏的),许多小改动的累积效应通常会使代码库变得凌乱、复杂和混乱。我们可以通过花一些额外的时间来帮助避免这种情况,每当我们因某事访问代码库时,重构和清理它。由于我们很少有机会从头开始重写系统,因此在很长一段时间内在微观改进上投入少量时间是保持系统健康的唯一可行方法。
 
简单
道给我们的第二个美德是节俭、谦虚、朴素:少做多,不杂乱。Go 本身是一种节俭简单的语言,具有最少的语法和表面积。它不会尝试做所有事情,或取悦所有人。
我们也应该这样做,让我们的程序小而集中,整洁,做好一件事。深度抽象为强大的机器提供了一个简单的接口。我们不会让用户做大量文书工作来获得调用我们库包的特权。只要我们可以为最常见的情况提供带有合理默认值的简单 API,我们就会这样做。
灵活性是一件好事,但我们不应该试图处理每一种情况,或者提供每一个特性。可扩展性很好,但我们不应该妥协一个简单的设计来允许我们还不需要的东西。事实上,一个简单的程序比一个复杂的程序更容易扩展。

我有最简单的口味。我总是对最好的感到满意。-奥斯卡·王尔德

我们不会用函数、类型、接口、回调、参数和选项来压倒用户。最小的 API 是最好的,因为它需要最少的知识才能使用。我们不会用数十个包和子文件夹的子文件夹使我们的模块复杂化。我们不需要无穷无尽的命令行标志或需要冗长的配置文件。
我们满足于重复代码块,而不是纯粹为了满足我们保持代码干燥的愿望而发明不必要的抽象。如果我们可以通过为几种不同类型实现相同的函数来解决问题,我们就不会编写复杂的代码生成器或通用函数。如果一个方法自然有点长,我们会让它长一点,而不是积极地将它重构为不必要的子函数,以便每个子函数都可以有几行长。
如果一个就够了,我们不会写十个测试。如果只需要一个函数,我们就不会创建接口。我们不让用户实现我们的接口;我们的目标是实现他们的接口。

出去的路是通过门。为什么没有人会使用这种方法?-孔子

我们是明确的;我们避免魔法。我们不会在没有帮助的地方使用并发。我们保持自包含,与其他包分离,我们避免让一个包或 API 的类型泄漏到我们代码库的其他部分。我们设定了强大的内部和外部界限并加以执行。
我们节约资源;我们避免泄漏并根据需要使用尽可能少的内存或 CPU。我们以流方式高效地处理数据,而不是将其放入大块内存中。我们生产的垃圾越少,需要收集的垃圾就越少。我们不会在不需要的地方传递上下文。
我们不执着于性能。走得很快。我们的代码不需要;至少,不会以牺牲简单性为代价。
正如 Go 谚语所说,我们接受接口值,因此我们需要对它们的内容做出最少的假设,但我们返回具体的值(结构),这样用户就无需编写大量关于它们的类型断言。
 
谦逊
第三件宝物是谦卑。道家如水,求低处,不争,不争,不去取悦他人。Go 本身是谦虚和务实的:它没有其他一些语言的所有高科技特性和理论优势。
事实上,它故意遗漏了许多其他语言的大卖点。它的设计者对创建一种令人印象深刻的编程语言或在人气投票中名列前茅的兴趣不大,而是希望为人们提供一个小而简单的工具,以便以最实用和最直接的方式完成有用的工作。
Go 认识到我们很容易出错,它有很多方法可以保护我们免受错误的影响。它负责分配内存,清理我们已经完成的事情,并警告我们未使用的导入或变量。它是一种专为那些知道自己并非无所不知,并且了解自己犯错倾向的人设计的语言:换句话说,就是谦虚的人。

最危险的错误是未能认识到我们自己的错误倾向。——罗勒·利德尔·哈特

 作为 Go 程序员,我们可以通过不要太聪明来保持谦虚。我们编写代码并不是为了让每个人都知道我们是多么了不起的程序员:相反,我们满足于做显而易见的事情。我们清楚而直接地表达自己,而无需在代码中突出我们自己的个性。
我们在解决问题时使用标准库,仅在没有解决问题时使用第三方库。如果某些东西有事实上的标准包,我们会使用它:如果它对其他人足够好,那么它对我们也足够好。
我们通过s.Exit or log.Fatal或panicking来避免意外终止用户的程序,因为我们认识到我们不够聪明,无法提前确定问题是否实际上是致命的。相反,我们会在它发生的地方处理我们所能处理的一切,当我们不能处理时,我们谦虚地返回一个错误,提供有用的上下文信息,并让我们的用户来决定要做什么。
我们可以认识到我们并不知道一切,我们无法做出非常准确的预测(尤其是关于未来),所以我们不应该浪费时间和精力预先设计我们可能永远不需要的东西。我们不认为我们最了解其他软件的人会如何希望使用,所以我们不会在依赖这种提前假设。
我们假设我们编写的任何内容都会包含错误,因此我们编写了仔细的测试,试图引发意外行为或不正确的结果。我们知道不可避免地会有一些我们不知道或无法正确预测的重要事情,因此我们不会针对现状过多地优化代码,因为很多工作最终都会被浪费掉。

采取学生的态度,永远不要问太多问题,永远不要学太多新东西。——奥格曼迪诺

当我们查看其他人的代码时,我们不会自动假设我们最了解:我们很高兴向任何可以教我们一些东西的人学习。如果某些事情看起来很奇怪或错误,我们会问“从什么角度来看这有意义?我为什么没有什么信息可以解释这是必要的?”
我们将我们的评论视为问题,以真诚而非讽刺的方式提出。这是必要的吗?如果……会发生什么?你有没有想过……?如果……会更好吗?我们像尊重自己的时间一样尊重他人的时间,因此我们不会要求他们提供不必要的信息,或者只是为了符合我们喜欢的风格而进行微小的更改,或者参加浪费的备考会议或撰写多余的状态报告。
我们知道我们并不总是正确的。理性的人可以以文明和建设性的方式对事情产生分歧。如果我们像对待白痴一样对待人们,那么当他们反应不佳时就不足为奇了。相反,我们首先假设对方是理性的、正派的,并且基于他们对情况的最佳理解而真诚地行事。有时情况并非如此,但它仍然是正确的默认假设,直到他们最终证明并非如此。

我们很谦虚地在要求其他人之前检查我们自己的代码,因为如果我们不想被打扰,他们为什么要检查我们代码?
我们花时间逐行​​阅读它,像新用户或开发人员一样阅读它,试图从逻辑上理解论点。是否清楚从哪里开始阅读?程序是否在开始时介绍了关键类型或常量,然后继续展示它们是如何使用的?事物的名称是否清晰准确地标识了它们的作用,或者它们是否通过十几个重构变得混乱和过时?程序是否整齐而自然地融入其结构,或者它的某些部分过度生长而其他部分奇怪地空了?
因为我们不受自己的聪明和优雅的束缚,所以我们不需要将三四个不同的想法塞进一行代码中。相反,我们清楚地、简单地、明显地、一步一步地、逐句地阐述了逻辑,以读者期望的方式完全按照读者期望的方式完成必要的事情。如果这是不可能的,我们会不厌其烦地向读者解释他们需要知道什么才能理解正在发生的事情。
因为我们知道我们不是天才,而且我们不能写出如此出色以至于不言自明的程序,所以我们对我们的解释感到有些麻烦。我们将代码与文档一起提供,这些文档不仅展示了程序的功能,还展示了如何用它完成用户可能想要做的事情。我们提供了详细的使用示例,准确地显示了从头开始执行现实任务需要做什么、用户在完成时应该看到什么以及接下来应该做什么,并且我们严格定期检查这些示例以确保它们还在工作。
 
无为 
我们已经讨论了一些方法,可以将善良、简单和谦逊的三宝应用到用 Go 编写软件中。这些品质已经存在于每个人身上,即使它们在某些人身上隐藏得很好。同样,每个人都已经知道如何在编程和生活中遵循道。事实上,他们不能做其他事情。但是一旦你理解了这个事实,生活就会变得更加有趣,不要再让一切都变得如此挣扎了。
道的最终教义是wúwéi(“不努力”)。这有时会被误解为懒惰、退缩或被动;恰恰相反。努力工作并不总是意味着工作得很好。我们都知道有些人长期很忙,总是匆匆忙忙、大惊小怪,忙得不可开交,但他们似乎从来没有真正取得过任何成就。此外,他们过得很痛苦,因为他们也知道这一点。
相反,当别人觉得我们什么都不做时,我们往往会尽最大努力:在美好的一天在河边散步,或者坐在门廊上看蜘蛛织网。如果我们能足够聪明停止尝试,正确的观念常常浮现在我们的头上,立竿见影。
与其把每一个问题都看成是要攻的敌人、要爬的山、要拆的墙,我们可以使用“不争”的原则(有时“不强迫”会更好的翻译)。我们可能都有过这样的尴尬经历:推一扇顽固的门,结果却发现这扇门对拉动的反应更好。我们在日常工作中忽略了哪些应该拉而不是推的小迹象?
解决问题的心态是好的,但问题消除,甚至更好。我们怎样才能重新定义这个问题,让它消失呢?需求的哪些重述会使解决方案变得微不足道甚至显而易见?是否有一个我们没有看到的简单而优雅的设计,因为我们专注于一些事实证明无关紧要的细节?我们可以不尝试解决这个问题就离开吗?最好的优化是根本不做这件事。
将编程与打字混淆是一个常见的错误。如果有人只是坐在那里盯着太空,那看起来他们并没有做任何有用的事情。但是,如果他们在键盘上疯狂地嘎嘎作响,我们认为他们正在取得一些成就。事实上,真正的编程发生在打字之前,有时而不是打字。当我们完成了一些非常好的编程后,通常我们唯一需要按下的键就是删除键。
 
真正有趣的是,您无需相信我的话,就可以肯定道家原则在编程或其他生活领域中的有效性。世界本身会教你什么有效,什么无效,以及如何区分。做一些小实验来锻炼你的善良、简单和谦逊,看看会发生什么,以及你对它的感受。
你不必称它为道,如果这激怒了你。这只是某人编造的一个词。如果你一直都知道做事有正确的方法和错误的方法,并且你不认为我说任何新的或有价值的东西,因为我有一个花哨的中文名字,你是对的。
下次你遇到问题时,试着不要努力或强迫一次,看看是否可以温和地鼓励问题自行解决。如果您发现自己很难将水牛带到您想去的地方,请停止挣扎。问问自己,您是否能找到水牛想去的地方,也许那毕竟不是它的最佳去处。