程序员不得不知的定律法则和模式

最近在Github上看到一个有意思的repository——hacker-laws(项目链接:https://github.com/dwmkerr/hacker-laws)

规则目录如下:

定律

  • 阿姆达尔定律(Amdahl's Law)
  • 布鲁克斯定律(Brooks's Law)
  • 康威定律(Conway's Law)
  • 霍夫施塔特定律(Hofstadter's Law)
  • 炒作周期 & 阿玛拉定律(The Hype Cycle & Amara's Law)
  • Hyrum定律(Hyrum's Law (The Law of Implicit Interfaces))
  • 摩尔定律(Moore's Law)
  • 帕金森定律(Parkinson's Law)
  • Putt's Law
  • 复杂性守恒定律(泰斯勒定律)(The Law of Conservation of Complexity (Tesler's Law))
  • 抽象漏洞定律(The Law of Leaky Abstractions)
  • 琐碎定律(The Law of Triviality)
  • Unix哲学
  • Spotify模型(The Spotify Model)
  • 沃德勒定律(Wadler's Law)

原则

  • 鲁棒性原则(The Robustness Principle (Postel's Law))
  • SOLID
  • 单一责任原则(The Single Responsibility Principle)
  • 开放/封闭原则(The Open/Closed Principle)
  • 利斯科夫替代原则(The Liskov Substitution Principle)
  • 接口隔离原则(The Interface Segregation Principle)
  • 依赖反转原则(The Dependency Inversion Principle)

接下来详细介绍这些定律原则

定律

1. 阿姆达尔定律

维基百科:计算机科学界的经验法则,因吉恩·阿姆达尔而得名。它代表了处理器并行运算之后效率提升的能力。阿姆达尔定律是固定负载(计算总量不变时)时的量化标准。

举例说明,如果一个程序由两部分(A和B)组成,A必须有单个处理器执行,B可以并行执行,那么在执行该程序的系统中增加多个处理器带来的好处是有限的。它可能会大大提升B的速度,但A的速度会保持不变。如下如所示:

amdahls_law.png
图片参考:By Daniels220 at English Wikipedia, Creative Commons Attribution-Share Alike 3.0 Unported, https://en.wikipedia.org/wiki/File:AmdahlsLaw.svg

可以看出,即使是50%可并行化的程序也将受益于10个处理单元,其中95%可并行化的程序仍然可以通过超过一千个处理单元实现显着的速度提升。

随着摩尔定律的减慢,以及单个处理器速度的加速减慢,并行化是提高性能的关键。 图形编程是一个很好的例子 - 使用基于Shader的现代计算,可以并行渲染单个像素或片段 - 这就是为什么现代图形卡通常具有数千个处理核心(GPU或Shader单元)。

2. 布鲁克斯定律

维基百科:将人力资源添加到后期软件开发项目中会使其更晚。

该定律表明,在许多情况下,试图通过增加更多人来加速已经迟到的项目的交付,将使交付甚至更晚。布鲁克斯清楚地表明这是一种过度简化,一般想法是,由于时间和通信开销的增加,在短期内效率会降低。而且,许多任务可能不是可分的,即容易在更多资源之间分配,这意味着潜在的效率也会更低。

交付中的常见短语“九个女人不能在一个月内生孩子”与布鲁克斯定律有关,特别是某些工作不可分割或平行的事实。

3. 康威定律

该定律提出系统的技术边界将反映组织的结构。在查看组织改进时,通常会提到它,Conway定律表明,如果一个组织由许多小的、断开连接的单元组成,那么它就是生成的软件。如果一个组织更多地建立在围绕特征或服务的“垂直”周围,那么软件系统也会反映这一点。

4. 霍夫施塔特定律

维基百科:即使考虑到霍夫施塔特定律,它也总是比你预期的要长。

在查看需要多长时间的估计时,你可能会听到此定律。在软件开发中似乎不言而喻,我们往往不能很好准确地估计需要多长时间才能完成。

5. 炒作周期 & 阿玛拉定律

维基百科:我们倾向于高估短期内技术的影响,并低估长期效应。(Roy Amara)

炒作周期是Gartner最初提出的技术兴奋和发展的直观表现。它可以很好用图来显示:

gartner_hype_cycle.png
图片参考:By Jeremykemp at English Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=10547051

简而言之,这个周期表明,新技术及其潜在影响通常会引发一阵兴奋。 团队经常快速进入这些技术,有时会发现自己对结果感到失望。 这可能是因为该技术还不够成熟,或者现实应用还没有完全实现。 经过一段时间后,技术的能力提高,使用它的实际机会增加,团队最终可以提高工作效率。 罗伊·阿马拉(Roy Amara)简洁地总结了这一点 ——“我们倾向于高估短期内技术的影响,并低估长期效应”。

6. Hyrum定律

维基百科:如果某个API有足够数量的用户,那么你在合同中承诺的并不重要:因为系统的所有可观察行为都将取决于某个人。(Hyrum Wright)

Hyrum定律指出,当你拥有足够数量的API消费者时,API的所有行为(即使那些未被定义为公共合同的行为)最终都会被某人依赖。一个简单的例子可能是非功能元素,例如API的响应时间。一个更微妙的例子可能是依赖于将正则表达式应用于错误消息以确定API的错误类型的消费者。即使API的公共合同没有说明消息的内容,指示用户应该使用相关的错误代码,一些用户可能会使用该消息,并且更改消息实质上会破坏这些用户的API。

7. 摩尔定律

维基百科:集成电路中的晶体管数量大约每两年翻一番。

通常用于说明半导体和芯片技术提高的绝对速度,从20世纪70年代到21世纪后期,摩尔的预测已被证明是高度准确的。近年来,这种趋势略有变化,部分原因是对组件小型化程度的物理限制。然而,并行化的进步,以及半导体技术和量子计算的潜在革命性变化可能意味着摩尔定律可能在未来几十年内继续保持正确。

8. 帕金森定律

维基百科:工作范围扩大可以填补完成时间。

在其原始背景下,该定律是基于对官僚机构的研究。它可能会悲观地应用于软件开发计划,理论是团队在截止日期之前效率低下,然后在截止日期前赶紧完成工作,从而使实际截止日期有些随意。

如果该定律与霍夫施塔特定律相结合,则会达到更加悲观的观点——工作将扩大以填补完成所需的时间,但仍需要比预期更长的时间。

9. Putt's Law

维基百科:技术由两类人主导,一类是理解他们不管理东西的人,另一类是管理他们不理解东西的人。

这些陈述表明,由于各种选择标准和群体组织方式的趋势,技术组织的工作层面将有一些技术人员,以及一些不了解他们正在管理的工作复杂性和挑战的管理角色的人员。这可能是由于彼得原则或迪尔伯特定律等现象造成的。但是,应该强调的是,诸如此类的法律是一种广泛的概括,可能适用于某些类型的组织,而不适用于其他组织。

10. 复杂性守恒定律(泰斯勒定律)

该定律规定系统中存在一定程度的复杂性不能减少。系统中的某些复杂性是“无意的”。这是由于结构不良、错误或者只是解决问题的糟糕建模造成的,可以减少(或消除)无意的复杂性。然而,由于要解决的问题固有的复杂性,某些复杂性是“内在的”, 这种复杂性可以移动,但不能消除。

该定律的一个有趣的地方是即使通过简化整个系统,内在的复杂性也不会降低,它会转移给用户,用户必须以更复杂的方式行事。

11. 抽象漏洞定律

维基百科:在某种程度上,所有非常规的抽象都是漏洞。(Joel Spolsky)

该定律指出,通常用于计算以简化复杂系统而处理的抽象将在某些情况下在底层系统中“泄漏”元素,这使得抽象表现为意外的方式。

一个例子可能是加载文件并读取其内容。文件系统API是较低级别内核系统的抽象,它们本身是与磁盘(或SSD的闪存)上的数据更改相关的物理过程的抽象。在大多数情况下,处理文件(如二进制数据流)的抽象将起作用。但是,对于磁驱动器,顺序读取数据将明显快于随机访问(由于页面错误的开销增加),但对于SSD驱动器,此开销不会出现。需要理解基础细节以处理这种情况(例如,数据库索引文件的结构可以减少随机访问的开销),开发人员可能需要注意的抽象“泄漏”实现细节。

当引入更多抽象时,上面的示例会变得更复杂。 Linux操作系统允许通过网络访问文件,但在本地表示为“普通”文件。如果存在网络故障,这种抽象将“泄漏”。如果开发人员将这些文件视为“正常”文件,而不考虑它们可能会受到网络延迟和故障的影响,那么解决方案就会出错。

描述该法律的文章表明,过度依赖抽象,加上对基础过程的不了解,实际上使得在某些情况下处理手头的问题变得更加复杂。

12. 琐碎定律

该定律表明,群体将给予更多的时间和注意力来处理琐碎或美容问题,而不是严肃而实质性的问题。使用的常见虚构的例子是委员会批准核电站的计划,他们大部分时间都在讨论自行车棚的结构,而不是电厂本身更为重要的设计。如果没有高度的主题专业知识或准备,很难就有关非常大的复杂主题的讨论提供宝贵的意见。但是,人们希望看到人们提供宝贵的意见。因此,倾向于将过多的时间集中在小细节上,这可以很容易地推理,但不一定特别重要。上面的虚构例子导致使用“自行车脱落”这个词作为浪费时间在琐碎细节上的表达。

13. Unix哲学

Unix哲学认为软件组件应该很小,并专注于做一件特定的事情。通过将小型、简单、定义良好的单元组合在一起,而不是使用大型、复杂的多用途程序,这样可以更轻松地构建系统。像“微服务架构”这样的现代实践可以被认为是这种定律的应用,其中服务很小,集中并做一件特定的事情,允许复杂的行为由简单的构建块组成。

14. Spotify模型

Spotify模型是团队和组织结构的一种方法,已被“Spotify”推广。在此模型中,团队围绕功能而非技术进行组织。Spotify模型还普及了段落、行会、章节的概念,这些是其组织结构的其他组成部分。

15. 沃德勒定律

在任何语言设计中,在任何语言设计中,花在讨论列表中某个特性上的总时间都与位置的二次幂成正比。

  1. 语义
  2. 句法
  3. 词法语法
  4. 评论的词法语法

简而言之,对于语义上花费的每一个小时,将在评论的语法上花费8小时。类似于“琐碎定律”,沃德勒定律指出,在设计一种语言时,与这些特征的重要性相比,与花在语言结构上的时间是不成比例的。

原则

原则通常更可能是与设计相关的准则。

1. 鲁棒性原则

维基百科:保守自己的所做所为,但是要接受别人的自由。

通常应用于服务器应用程序开发中,该原则指出你发送给其他人的内容应尽可能小且符合要求,但如果可以处理,则应该瞄准允许不符合要求的输入。该原则的目标是构建稳健的系统,因为如果仍然可以理解意图,它们可以处理形成不良的输入。但是,接受格式错误的输入可能存在安全隐患,特别是如果此类输入的处理未经过充分测试。

2. SOLID

这是一个缩写,指的是:

  • S:单一责任原则
  • O:开放/封闭原则
  • L:Liskov替代原则
  • I:接口隔离原理
  • D:依赖性倒置原则

这些是面向对象编程的关键原则。诸如此类的设计原则应该能够帮助开发人员构建更易于维护的系统。

3. 单一责任原则

维基百科:每个模块或类只应承担一项责任。

这是第一个'SOLID'原则。这个原则表明模块或类只应该做一件事,并且只有一件事。更实际的是,这意味着对程序功能的单个小的更改应该只需要更改一个组件。例如,更改密码验证复杂性的方式应该只需要更改程序的一部分。

从理论上讲,这应该使代码更健壮,更容易修改。知道正在改变的组件只有一个责任,这意味着测试该改变应该更容易。使用前面的示例,更改密码复杂性组件应该只能影响与密码复杂性相关的功能。对于具有许多职责的组件的变更影响可能要困难得多。

4. 开放/封闭原则

维基百科:实体应开放以进行扩展并关闭以进行修改。

这是第二个'SOLID'原则。这个原则规定实体(可以是类,模块,函数等)应该能够扩展它们的行为,但是它们的现有行为不应该被修改。

作为一个假设的例子,想象一个能够将Markdown文档转换为HTML的模块。如果可以扩展模块以处理新建议的降价功能,而无需修改模块内部,则可以进行扩展。如果消费者无法修改模块以便处理现有的Markdown功能,那么它将被关闭以进行修改。

这个原则与面向对象编程特别相关,我们可以设计对象以便于扩展,但是可以避免设计可能以意想不到的方式改变其现有行为的对象。

5. 利斯科夫替代原则

维基百科:应该可以在不破坏系统的情况下用子类型替换类型。

这是第三个'SOLID'原则。该原则指出,如果组件依赖于类型,那么它应该能够使用该类型的子类型,而不会导致系统失败或必须知道该子类型的详细信息。

举个例子,假设我们有一个方法,它从表示文件的结构中读取XML文档。如果该方法使用基类型'file',则应该能够在函数中使用从'file'派生的任何内容。如果'file'支持反向搜索,并且xml解析器使用该函数,但是当尝试反向搜索时派生类型'network file'失败,则'network file'将违反该原则。

该原则与面向对象的编程特别相关,其中必须仔细建模类型层次结构以避免混淆系统的用户。

6. 接口隔离原则

维基百科:不应该强迫任何客户端依赖它不使用的方法。

这是第四个'SOLID'原则。该原则指出组件的使用者不应该依赖于它实际上不使用的那个组件的功能。

作为一个例子,假设我们有一个方法从表示文件的结构中读取XML文档。它只需要读取文件中的字节,向前移动或向后移动。 如果由于文件结构的不相关功能发生更改(例如更新用于表示文件安全性的权限模型)而需要更新此方法,则原则已失效。文件最好实现“可搜索”接口,并让XML读者使用它。

该原理与面向对象的编程特别相关,其中使用接口,层次结构和抽象类型来最小化不同组件之间的耦合。Duck typing是一种通过消除显式接口来强制执行此原则的方法。

7. 依赖反转原则

解释:高级模块不应该依赖于低级实现。

这是第五个'SOLID'原则。该原则指出,更高级别的协调组件不应该知道其依赖关系的细节。

例如,假设我们有一个程序从网站读取元数据。我们假设主要组件必须知道下载网页内容的组件,然后是可以读取元数据的组件。如果我们考虑依赖性反转,主要组件将仅依赖于可以获取字节数据的抽象组件,然后是一个能够从字节流中读取元数据的抽象组件。主要组件不了解TCP / IP,HTTP,HTML等。

这个原则很复杂,因为它似乎可以“反转”系统的预期依赖性(因此名称)。实际上,它还意味着单独的编排组件必须确保使用抽象类型的正确实现(例如,在前面的示例中,某些东西仍必须为元数据读取器组件提供HTTP文件下载器和HTML元标记读取器)。然后,它涉及诸如控制反转和依赖注入之类的模式。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值