软件开发中的重要原则和设计指南

278 阅读8分钟

KISS(保持简单)

大多数系统在保持简单而不复杂的情况下运作得最好。

为什么:

  • 代码越少,编写时间越短,bug越少,修改起来也更容易。
  • 简单即是至臻的优雅。
  • 看起来完美并不是在没有东西可添加时实现的,而是在没有东西可删减时实现的。

资源:

YAGNI(你不会需要它)

YAGNI代表“你不会需要它”,在必要之前不要实现它。

为什么:

  • 任何只为明天可能需要的功能而做的工作,意味着在当前迭代中失去了完成其他功能的努力。
  • 它会导致代码膨胀,软件变得更大更复杂。

如何:

  • 只有在真正需要时才实现功能,而不是仅仅预见到需要它们时才实现。

资源:

做最简单可能的事情

为什么:

  • 只有真正解决实际问题的简单事情才能取得真正的进展。

如何:

  • 问自己:“最简单可能的事情是什么?”

资源:

关注点分离

关注点分离是一种将计算机程序分成不同部分的设计原则,以便每个部分都涉及一个不同的关注点。

为什么:

  • 简化软件应用程序的开发和维护。
  • 当关注点良好分离时,可以独立地重用、开发和更新各个部分。

如何:

  • 将程序功能分解为尽可能少重叠的单独模块。

资源:

保持DRY(不要重复自己)

为什么:

  • 重复(无意或有意的重复)可能导致维护困难、代码重构问题和逻辑矛盾。
  • 对系统的任何单个元素进行修改不需要改变其他逻辑上无关的元素。

如何:

  • 将业务规则、长表达式、if语句、数学公式、元数据等放在一个地方。

资源:

为维护者编写代码

为什么:

  • 维护是任何项目中最昂贵的阶段。

如何:

  • 成为维护者。
  • 编写代码和注释时,要以维护代码的人是一个知道您住在哪里的暴力疯子为前提。
  • 始终以编写代码和注释的方式,使得稍微低级的人可以愉快地阅读和学习它。

资源:

避免过早优化

为什么:

  • 预先不知道瓶颈在哪里。
  • 优化后,阅读代码可能更困难,从而使维护更加困难。

如何:

  • 使其正常工作,使其正确,使其快速
  • 在需要时再进行优化,仅在进行性能分析并发现瓶颈后再进行优化。

资源:

最小化耦合

为什么:

  • 一个模块的更改通常会导致其他模块的连锁反应更改。
  • 由于增加了模块间的依赖关系,模块的组装可能需要更多的工作和/或时间。
  • 由于必须包含依赖模块,某个特定模块可能更难被重用和/或测试。
  • 开发人员可能害怕更改代码,因为他们不确定可能受到什么影响。

如何:

  • 消除、最小化和减少必要关系的复杂性。

资源:

迪米特法则

为什么:

  • 这通常会加强耦合度。
  • 它可能会暴露太多的实现细节。

如何:

  • 对象的方法只能调用以下方法:
    • 对象本身的方法。
    • 方法的参数。
    • 在方法中创建的任何对象。
    • 对象的直接属性/字段。

资源:

组合优于继承

为什么:

  • 类之间的耦合度较低。
  • 使用继承,子类容易做出假设,并且违反了LSP。

如何:

  • 测试LSP(可替换性)以决定何时继承。
  • 当存在“具有”(或“使用”)关系时使用组合,当存在“是”关系时使用继承。

资源:

正交性

关于正交性:

  • 正交性的基本思想是在系统中不相关的概念之间不应该有关联。
  • 它与简单性有关;设计的正交性越高,例外情况越少。这使得学习、阅读和编写编程语言中的程序更容易。正交特性的含义与上下文无关;关键参数是对称性和一致性。

资源:

稳健性原则

为什么:

  • 为了能够发展服务,您需要确保提供者可以进行更改以支持新的需求,同时对现有客户端的破坏最小。

如何:

  • 发送命令或数据到其他计算机(或同一台计算机上的其他程序)的代码应完全符合规范,但接收输入的代码应接受非符合规范的输入,只要意义清楚。

资源:

控制反转

为什么:

  • 控制反转用于增强程序的模块化并使其可扩展。
  • 将任务的执行与实现分离。
  • 将模块专注于其设计的任务。
  • 使模块不依赖于其他系统如何执行其工作,而是依赖于契约。
  • 在替换模块时防止副作用。

如何:

  • 使用工厂模式
  • 使用服务定位器模式
  • 使用依赖注入
  • 使用上下文查找
  • 使用模板方法模式
  • 使用策略模式

资源:

最大化内聚性

为什么:

  • 理解模块变得更加困难。
  • 维护系统变得更加困难,因为域中的逻辑变化会影响多个模块,并且因为一个模块的更改需要相关模块的更改。
  • 重用模块变得更加困难,因为大多数应用程序不需要模块提供的随机操作集。

如何:

  • 将共享单一责任的相关功能分组(例如,在一个类中)。

资源:

Liskov替换原则

LSP关注对象的预期行为:

  • 程序中的对象应该可以替换为其子类型的实例,而不会改变程序的正确性。

资源:

开闭原则

为什么:

  • 通过最小化对现有代码的更改来提高可维护性和稳定性。

如何:

  • 编写可扩展的类(与可以修改的类相对)。
  • 仅公开需要更改的可移动部分,隐藏其他所有内容。

资源:

单一责任原则

为什么:

  • 可维护性:只有在一个模块或类中需要进行修改。

如何:

  • 应用Curly's Law。

资源:

隐藏实现细节

为什么:

  • 当实现发生变化时,接口客户端使用的接口不需要更改。

如何:

  • 最小化类和成员的可访问性。
  • 不要在公共环境中公开成员数据。
  • 避免将私有实现细节放入类的接口中。
  • 减少耦合以隐藏更多的实现细节。

资源:

Curly's Law(卡利法则)

Curly's Law是关于为任何特定的代码目标选择一个单一、明确定义的目标的法则:只做一件事。

资源:

封装变化

为什么:

  • 在发生变化时最小化所需的修改。

如何:

  • 封装可变概念在API后面。
  • 可能将可变概念分离到其自己的模块中。

资源:

接口隔离原则

为什么:

  • 如果一个类实现了不需要的方法,调用方需要知道该类的方法实现。例如,如果一个类实现了一个方法,但只是简单地抛出异常,那么调用方将需要知道这个方法实际上不应该被调用。

如何:

  • 避免臃肿的接口。类不应该被迫实现违反单一责任原则的方法。

资源:

童子军法则

美国童子军有一个简单的规则,我们可以应用到我们的职业中:“离开营地时比你来时干净”。童子军法则表示,我们应该始终比我们找到的代码更加清晰。

为什么:

  • 当对现有代码库进行更改时,代码质量往往会下降,累积技术债务。遵循童子军法则,我们应该在每次提交时关注代码质量。通过持续重构,无论规模大小,抵制技术债务。

如何:

  • 确保每次提交都不会降低代码库的质量。
  • 每当有人发现某个代码不够清晰时,都应该抓住机会立即修复。

资源:

命令查询分离

命令查询分离原则指出,每个方法应该是一个执行操作的命令或返回数据给调用者的查询,但不应同时具备两者。提问不应修改答案。

为什么:

  • 通过明确将方法分为查询和命令,程序员可以在不了解每个方法的实现细节的情况下编写代码。

如何:

  • 将每个方法实现为查询或命令。
  • 应用命名约定来暗示方法是查询还是命令。

资源:

墨菲定律

任何可能出错的事情终将出错。

资源:

布鲁克斯定律

在一个已经延期的软件项目上增加人手只会使项目进一步延期。

资源:

林纳斯定律

只要有足够多的眼睛,所有的错误都是肤浅的。

资源: