【译】好软件设计的7个美德

103 阅读11分钟

原文链接:dzone.com/articles/th…

每个好的软件都具有七个黄金美德:有效性,健壮性,可维护性,灵活性……

要生成高质量,可靠和可扩展的软件,必须学习和遵循许多良好的实践,设计模式,代码检查等。

但是他们每个人都为这七个黄金美德中的一个或几个服务**:**

  1. 效用
  2. 鲁棒性/安全性/可靠性
  3. 可维护性
  4. 灵活性
  5. 可重用性/可移植性
  6. 可扩展性
  7. 效率

在本文中,我将解释这些优点中的每一个的含义,并引用其中涉及的一些众所周知的良好做法。

为什么这个命令很重要?

我已经写下了这七个美德,根据需要对它们进行了排序,这是更必要的。但我将以向后的顺序进行说明。

假设我(一个客户)请求一个程序,并且给了我一个软件,该软件在技术上是完美的。

哦,好的:据说这是完美的,但是我知道在现实世界中没有什么是完美的。因此,如果它必须失败,我宁愿它以效率失败,原因有两个:

  1. 如果程序拥有其他六个优点,那么开发最终的优化将容易得多。
  2. 尽管效率失败,但是,如果程序仍可扩展,我将有机会增加基础平台的功能以获得所需的吞吐量。

如果可伸缩性失败,但是程序具有可重用的部分,则我可以确定代码中的瓶颈,并用更好的组件替换它们,同时仍然重用其余部分。

如果该程序不可**重用,**但它很灵活,那么我可以调整我的数据,以便只要足够灵活,就可以由现有例程对其进行处理。

如果该程序既不灵活,则希望它至少是可维护的,因为这样我就可以对其进行探索,发现问题并进行必要的更改并构建另一个固定版本。

如果程序是一堆乱七八糟的代码行,没有可读性,组织或文档,那么我希望它至少是健壮的,因此几乎没有可能需要修复。

如果它既不健壮,我希望它至少是有效的,因为一个有效的程序将运行一段时间,直到发现一些弱点。而且,即使在这种情况下,程序的其他部分仍可能会起作用。

最后,如果该程序甚至没有效果,那么除了从头开始编写另一个新程序之外,别无其他。而这次,确保它实现了设计七个优点

计划化

从计划的角度来看,有一些稍微不同的方法:

  • 如果您只是设计一个无法确定其未来的原型,那么请关注有效性,健壮性,可维护性,灵活性和可重用性。这应该足以产生具有良好成功前景的可靠,可部署的软件。并且,如果演示成功完成,并且因此您的软件将得以发展,那么您应该能够以最小的努力使其可扩展且高效。
  • 如果您打算设计一个可以在短期内保持稳定(具有已知的使用寿命即将结束)的程序,则应着眼于有效性,健壮性,可维护性,可扩展性以及(如果需要)效率(是的,可维护性,因为总是需要进行纠正性修复)。
  • 而且,如果您打算长期发展进一步的发展,那么请把您的全部七个美德都投入关注。

1.效力

看起来似乎很明显,但是代码的第一个(也是主要的)优点是,它在功能上恰当的,即它的行为符合预期。当您的程序对用户没有用时,您不能期望它的快速性或可重用性将使它受到赞赏。

但是,由于功能问题,不能通过良好的技术规范来指定。这更多是一个测试问题。

2.坚固/安全/可靠

健壮性是对程序进行自动保护以防止错误或任何条件的安全,此类条件会使程序在预期的流程之外运行。一些常见的原因是数据丢失,出现空值,格式错误或数据不一致。结果可能会突然终止正在运行的线程,耗尽内存,进入无限循环,甚至在不希望的时刻跳转到有效例程。

获得强大程序的一些规则:

  • 最小化类中成员的可见性:默认情况下为私有,除非需要通过子类访问(然后应该对其进行保护),除非需要访问每个包(然后应该为公共)。

  • 在线程之间共享数据时,请适当保护它,以避免数据冲突。

  • 始终保持变量的一致性:每个变量必须随时包含一个

    有效值

    。这意味着在该值有效之前,您不应将任何值加载到变量中。这特别意味着在初始化时不存储默认值(如null)。

    • **类成员变量:**最好在构造函数中初始化,并提供所有必需的值。
    • **局部变量:**在方法内,最好延迟变量声明,直到有一个有效的初始值为止。 (我承认在某些情况下,例如对状态机进行编码,在其中值得存储空值以避免过多的程序复杂性)。
  • 要小心大对象创建不排气存储器,特别是那些意味着连续的存储器分配(阵列和基于阵列的结构:StringStringBuilderArrayListArrayDeque,等等)。相反,请考虑使用基于链接的替代实现。在处理大文件时,请始终选择基于流的算法。

  • 编码循环时,请尝试在声明中建立一个清晰可靠的退出条件。避免最初存在内部异常退出条件的无限循环。

  • 始终选择强类型:不要使用String类型,而是用于存储透明的用户数据(以及内部ID)。对于所有其他数据,选择了最具体类型可供选择:intdoubleDateURLPath,等。

  • 请遵循Postel的规则:为输入参数选择最通用的类型,为输出参数和返回值选择最特定的类型。

  • 如有必要,请尝试避免递归:堆栈耗尽中存在大量迭代的风险。

  • 使用递归结构时,请避免出现指针循环的可能性(例如,在每次迭代中提供输入的输入参数以跟踪遍历状态)。

  • 提供一整套具有最大代码覆盖率的单元测试(请阅读我即将发表的有关单元测试的文章)。

推荐模式:无状态,枚举。

3.可维护性

几乎不解自明。可维护性是指程序易于开发人员阅读,编辑和修改。由于(几乎)没有任何程序比第一个版本100%完美,因此迟早需要手动编辑以更正错误或进行改进。

一些使代码可维护的规则:

  • 最小化每个范围内的声明数:
    • 最小化方法中的行(如有必要,在其他方法中分配它们)。
    • 最小化类/接口中的成员(如有必要,在其他类中分配它们)。
    • 最小化包中的类/接口(如有必要,在其他包中分发它们)。
    • 最小化库中的包(如有必要,在其他库中分发它们)。
    • 最小化部署中的库(考虑多部署分布式应用程序)。
  • 坚持标准命名风格。
  • 避免重复代码。取而代之的是,为每个设计的抽象赋予具体的责任,并提供一个公共/受保护的API,该API应在需要的任何地方重用。
  • 自动文档化是最好的文档,但是内联注释始终是受欢迎的(只要它们写得很好并且有帮助)。

推荐模式:抽象,继承,组合。

4.灵活性

当程序不需要严格的参数或前提条件时,它就是灵活的。除其他外,这允许以不同方式使用同一例程,或处理不同种类的数据。

一些有助于获得灵活性的做法是:

  • 请遵循Postel的规则:为公共声明(输入参数,返回值)选择最通用的类型。
  • 始终基于接口进行设计:通过一个或几个公共接口对模块的主要功能进行建模,并通过这些接口在模块之间建立依赖关系。
  • 组合类时,应优先考虑组合而不是继承。
  • 每个模块至少应包含:
    • 对于每种功能,都需要一个公共接口以及与服务一样多的公共方法。
    • 接口的一种抽象实现,以帮助开发人员将其子类化。
    • 一个公共例外,可能会发生与例外情况一样多的子类。
    • 一个公共工厂来生成接口的实例。

灵活性不能太高。它的局限性是:

  • 为了始终在方法/类/模块之间保持较高的凝聚力。
  • 为了始终保持方法/类/模块之间的低耦合。

推荐模式:抽象,工厂,抽象工厂,组成,继承,侦听器,映射的进程。

5.可重用性/可移植性

当可以从各种程序或环境中执行API时,该API是可重用的。设计可重用代码时,最重要的规则是确保其**基于接口,**并且与其他程序/模块的耦合度低:

  • 最优选地,核心接口应该没有上层。
  • 它的方法应该具有低耦合性:它们的输入/输出参数类型最好属于标准运行时库。
  • 它们的使用应尽可能简单。

如果核心功能是根据这些条款设计的,那么我们只需封装核心接口即可构建所需的外观。例如:如果您打算编写命令行实用程序,请不要将核心功能放在main类中。相反,按照说明编写自己的接口,类和工厂,以便您获得可重用的核心模块。然后,添加一个CLImain类模块,该类具有从哪里调用内核的外观。这样,您可以在将来添加其他外观。

注意:此处设置的外观必须仅用于多态分配,其职责通常不只是转换和传递参数及错误。

推荐模式:抽象,外观,适配器,构图。

6.可扩展性

可扩展性定义为程序随着底层平台功能的增加而以相同(或可接受的相似比率)增加吞吐量的能力。

底层平台通常是根据硬件功能(CPU速度,内存速度和大小,网络带宽)来衡量的,但是在开发更高级别的应用程序时,您可能还会考虑到较低级别部分的功能:缓存大小,线程池,磁盘配额,软件容器,数据库的索引功能等。

扩展性通常遇到的障碍:

  • 程序中的瓶颈:处理多线程设计时,请尽量减少关键区域的数量和长度。避免进入关键区域的典型技术是重用缓存中的对象。
  • 内存滥用:处理大量数据时,避免需要连续的内存分配。总是喜欢流+缓冲,或者至少是segmentation
  • 大量资源依赖: 最大程度减少大量资源利用的次数和持续时间。

如果必须相对于硬件衡量可伸缩性,则必须一起评估程序和基础平台(操作系统,虚拟机等)(您不能在不可伸缩的平台上构建可伸缩的程序!)。

推荐模式:池,缓存,单例,流式传输。

7.效率

程序的效率(或性能)是它产生最大数量的结果并且仍然消耗最少数量的资源(通常是CPU时间和内存)的能力。从步进效力效率 被称为优化

这是有关优化的两个最重要的建议(读一本书):

  1. 第一:不要这样做。
  2. 第二:好的,如果必须这样做,请保留到最后。

如您所见,当我决定将效率作为软件设计/开发中要实现的最后一项优点时,我正在遵循这些明智的建议。原因很简单:优化可能是软件设计中最困难,最复杂的任务,因此很容易出错。“不做”明智的建议是一种尝试,它使您从一开始就将优化作为主要要求引入的初衷就失去了信心。

相反,第二条明智的建议会指导您首先构建一个运行良好的程序,并最终对其进行优化。怎么样?我的回答:首先确保它有效,健壮,可维护,灵活,可重用和可扩展。这样,引入优化可能会变得非常简单(例如,通过将效率较低的实现替换为更好的实现,由于使用了接口,该实现对于现有代码应该是透明的)。看看这七个美德是如何关联的?