第六章代码的可维护性——可维护性的度量和构造原则

1.软件的维护与演化

我们一直说软件维护,那么什么是软件维护呢?其实就是修改错误、改善性能的过程。运维是软件开发中最困难的工作之一,他需要处理各种来自用户报告的问题与故障。

软件维护主要针对一下几种(数据来源未知2333):

  • 纠错性25%
  • 适应性21%
  • 完善性50%
  • 预防性4%

“变化”在软件生命周期中是不可避免的!那么如何在最初的设计中充分考虑到未来的变化,避免因为频繁的变化导致软件复杂度增加和质量的下降呢?这就是我们这章要说的事情——提高软件的适应性,延续软件生命。注意软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了。所以在设计开发的过程中就要考虑到将来的可维护性,使设计方案容易改变。

这张将会着重讲解几个基于可维护性建设的例子:

  • 模块化
  • OO设计原则
  • OO设计模式
  • 基于状态的构造技术
  • 表驱动的构造技术
  • 基于语法的构造技术


2.可维护性的度量

首先来说几个常用的可维护性度量指标:

  • 圈复杂度(Cyclomatic Complexity):度量代码的结构复杂度。
  • 代码行数:……
  • 可维护性指数(Maintainnbility Index)MI:计算0到100之间的索引值。表示维护代码的相对容易性,高价值意味着更好地可维护性。
  • 继承的层次数(Depth of Inheritance):就是继承深度,英文更通俗些。
  • 类之间的耦合度(Class Coupling):通过参数,局部变量,返回类型,方法调用,泛型或模板实例化,基类,接口实现,在外部类型上定义的字段和属性修饰来测量耦合到唯一类。
  • 单元测试覆盖率(Unit test coverage):只是代码库的那些部分被自动化单元测试覆盖。

3. 模块化设计和模块化原则

其设计目的只有两点:

  • 模块内高内聚
  • 模块间低耦合

至于如何评估模块化也有五个标准:

  1. 可分解性(Decomposability)
  2. 可组合性(Composability)
  3. 可理解性(Understandability)
  4. 可持续性(Continulity)
  5. 出现异常之后的保护(protection)

模块化设计的五个规则:

  1. 直接映射
  2. 尽可能少的接口
  3. 尽可能小的接口
  4. 显示接口
  5. 信息隐藏

然后我们来说一下耦合与内聚的概念(Coupling and Cohension)

耦合是模块之间的依赖关系的度量。

对“依赖关系”解释:如果两个模块之间的变化可能需要另一个模块的变更,这两个模块之间存在依赖关系。

模块之间的耦合主要取决于:

  1. 模块之间接口的数量(质量)
  2. 每个接口的复杂度


而聚合是指衡量一个模块的功能或责任的强烈程度的一个指标。如果一个模块的一切的结构都朝着相同的目标努力,那么他就具有很高的聚合度。


模块化设计所追求的目标就是高内聚低耦合!


4.OO设计原则:SOLID

不不不!这可并不是固态原则……他是五个类设计原则的缩写:

  • (SRP)单一责任原则The Single Responsibility Principle
  • (OCP)开放-封闭原则The Open-Closed Principe
  • (LSP)Liskov替换原则The Liskov Substitution Principe
  • (ISP)接口聚合原则The Interface Segregation Principe
  • (DIP)依赖转置原则The Dependency Inversion Principe

SOLID原则!剩下的将主要围绕这五个原则分别说明:


A. 单一责任原则(SRP):

原则内容很简单,总结就是做好自己的事,不要插手别人的事。

什么意思?假设一个软件有一段代码,如果有维护软件的需求需要改动这段代码,那么也就可以理解为这段代码对这次改动负有责任。形象点说,如果软件如果出现了问题,通过改动这段代码修复了,那么这段代码就是要对这个问题负责。所以代码变化的根本原因是责任,代码负责的功能出现了问题才会变化。

所以为了减少代码变化,最好让一个类有一个责任而不是多个,也就是说做好自己的事。

反过来说如果一个类承担了多个责任,那么会引入额外的包,占据资源。同时会导致频繁的重新配置部署等。



2. 开放-封闭原则(OCP)

开放:指的是对拓展性开放,模块的行为应该是可拓展的,从而该模块可表现出新的行为以满足需求的变化。

封闭:指的是对修改封闭,模块自身的代码是不应被修改的,拓展模块的行为的一般途径是通过修改模块内部实现实现的。如果一个模块不能被修改,那么它通常被认为是具有固定的行为。

其核心思想就是抽象技术。

但运用的时候也会有一些情况,我们来看一下的栗子:


问题:


修改后:



3. Liskov替换原则(LSP)

其原则主要是:子类型必须能够替换其基类型。而且派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异。

这个原则已经在第五章中的复用性结构中详细说明了,不了解的可以看这里


4. 接口聚合原则(ISP)

其原则内容是:客户端不应依赖于他们不需要的方法。也就是说要吝啬你的接口方法,除去无用的。

如果接口过于庞大可以分解为多个小接口,不同的接口向不同的客户端提供服务,客户端只需要访问自己所需要的接口。



5.依赖转置原则(DIP)

其原则内容是:抽象的模块不应依赖于具体的模块,而是具体的模块应该依赖于抽象。

觉得课程中的栗子不是很恰当,所以就不截图了,这篇博客讲解的很详细,感兴趣的大家可以看一下。



5. OO设计原则GRASP

General Responsibility Assignment Software Patterns(Principes)通用责任分配软件模式

其是关于如何为“类”和“对象”指派“职责”的一系列原则。

对象的职责与对象的义务是有关联的。

大栗子:


责任是使用方法来实现的,makepayment代表sale对象有责任创建payment对象。


GRASP通常由以下几部分组成:

  • 控制器(Controller)
  • 信息专家(Information expert)
  • 创建者(Creators)
  • 低耦合(Low coupling)
  • 高内聚(High cohension)
  • 间接(Indirection)
  • 多态(polymorphism)
  • 受保护的变体(Protected variations)
  • 纯制造(Pure fabrication)


猜你喜欢

转载自blog.csdn.net/qq_37549266/article/details/80717924