软件构造系列学习笔记(6.1)————可维护性的度量和构造原则

原创 2018年04月16日 13:56:23

可维护性的度量和构造原则

本章面向另一个质量指标:可维护性——软件发生变化时,是否可以以很小的代价适应变化?
本节是宏观介绍:(1)什么是软件维护;(2)可维护性如何度量;(3)实现高可维护性的设计原则——很抽象。

目录

  • 软件维护和演变
  • 可维护性度量
  • 模块化设计和模块化原则
  • OO设计原则:SOLID
  • OO设计原则:GRASP

软件维护和演变

软件工程中的软件维护是交付后修改软件产品以纠正故障,提高性能或其他属性,简而言之,软件维护:修复错误、改善性能。

负责软件维护的工程师我们通常称为运维工程师,处理来自用户报告的故障/问题 。

软件维护的类型:

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

Lehman关于软件演化总结出的规律:软件质量下降,复杂度增加。
这里写图片描述

“变化”在软件生命周期中是不可避免的! 如何在最初的设计中充分考虑到未来的变化, 避免因为频繁变化导致软件复杂度的增加和质量的下降?

软件维护和演化的目标:提高软件的适应性,延续软件生命 。

软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了 。在设计与开发阶段就要考虑将来的可维护性 ,设计方案需要“easy to change”。

基于可维护性建设的例子:

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

这些会在接下来章节具体讲述。

可维护性的度量

可维护性:可轻松修改软件系统或组件,以纠正故障,提高性能或其他属性,或适应变化的环境。

除此之外,可维护性还有其他许多别名:可扩展性、灵活性、可适应性、可管理性、支持性。总之,有好的可维护性就意味着容易改变,容易扩展。

一些常用的可维护性度量标准:

  • 圈复杂度(CyclomaticComplexity):度量代码的结构复杂度。
  • 代码行数(Lines of Code):指示代码中的大致行数。
  • Halstead Volume:基于源代码中(不同)运算符和操作数的数量的合成度量。
  • 可维护性指数(MI):计算介于0和100之间的索引值,表示维护代码的相对容易性。 高价值意味着更好的可维护性。
  • 继承的层次数:表示扩展到类层次结构的根的类定义的数量。 等级越深,就越难理解特定方法和字段在何处被定义或重新定义。
  • 类之间的耦合度:通过参数,局部变量,返回类型,方法调用,泛型或模板实例化,基类,接口实现,在外部类型上定义的字段和属性修饰来测量耦合到唯一类。
  • 单元测试覆盖率:指示代码库的哪些部分被自动化单元测试覆盖。

模块化设计和模块化原则

模块化编程是一种设计技术,它强调将程序的功能分解为独立的可互换模块,以便每个模块都包含执行所需功能的一个方面。

设计的目标是将系统划分为模块并在组件之间分配责任,方式如下:

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

模块化降低了程序员在任何时候都必须处理的总体复杂性,因为模块化实现了分离关注点和信息隐藏。

内聚和耦合的原则可能是评估设计可维护性的最重要的设计原则。

评估模块化的五个标准

  • 可分解性
  • 可组合性
  • 可理解性
  • 可持续性
  • 出现异常之后的保护

模块化设计的五条原则

  • 直接映射
  • 尽可能少的接口
  • 尽可能小的接口
  • 显式接口
  • 信息隐藏

耦合和聚合

耦合是模块之间依赖关系的度量。 如果两个模块之间的变化可能需要另一个模块的变更,则两个模块之间存在依赖关系。

模块之间的耦合程度取决于:

  • 模块之间的接口数量(数量)
  • 每个接口的复杂性(由通信类型决定)(质量)

这里写图片描述

精心设计的Web应用程序模块化:

  • 指定数据和语义的HTML文件
  • 指定HTML数据外观和格式的CSS规则
  • 定义页面行为/交互性的JavaScript

这里写图片描述

聚合是衡量一个模块的功能或责任有多强烈程度的一个指标。如果一个模块的所有元素都朝着相同的目标努力,则它具有很高的聚合度。

最好的设计在模块内具有高内聚力(也称为强内聚力)和模块之间的低耦合(也称为弱耦合)。

OO设计原则:SOLID

SOLID:5类设计原则

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

单一责任原则

SRP:不应有多于1个的原因使得一个类发生变化;一个类,一个责任。

如果一个类包含了多个责任,那么将引起不良后果:引入额外的包,占据资源;导致频繁的重新配置、部署等。

SRP是最简单的原则,却是最难做好的原则。

SRP的一个反例:
这里写图片描述

开放-封闭原则

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

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

关键的解决方案:抽象技术。
使用继承和组合来改变类的行为。

OCP的一个反例:
这里写图片描述

再来看一个例子:

// Open-Close Principle - Bad example 
class GraphicEditor {
public void drawShape(Shape s) { 
    if (s.m_type==1) 
        drawRectangle(s); 
    else if (s.m_type==2) 
        drawCircle(s); 
    } 
    public void drawCircle(Circle r) 
        {....} 
    public void drawRectangle(Rectangle r) 
        {....} 
}

class Shape { int m_type; }
class Rectangle extends Shape { Rectangle() { super.m_type=1; } }
class Circle extends Shape { Circle() { super.m_type=2; } } 

上面代码中有几个问题:

  • 不可能在不修改GraphEditor的情况下添加新的Shape
  • GraphEditorShape之间的紧密耦合
  • 不调用GraphEditor就很难测试特定的Shape

改进之后代码如下:

// Open-Close Principle - Good example
class GraphicEditor { 
public void drawShape(Shape s) { 
    s.draw(); 
    } 
}
class Shape { abstract void draw(); }
class Rectangle extends Shape { public void draw() { // draw the rectangle } } 

Liskov替换原则

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

这条原则在第五章第二节(5.2)中已详细讲述过,这儿就不再赘述了。

依赖转置原则

高级模块不应该依赖于低级模块。 两者都应该取决于抽象。抽象的模块不应依赖于具体的模块,具体应依赖于抽象。

例子: the “Copy” program
这里写图片描述

通常我们会这么实现这个程序:

void Copy(OutputStream dev) { 
    int c; 
    while ((c = ReadKeyboard()) != EOF) 
        if (dev == printer) 
            writeToPrinter(c); 
        else 
            writeToDisk(c); 
}

改用抽象技术后:

interface Reader { public int read(); } 
interface Writer { public int write(c); } 
class Copy { 
    void Copy(Reader r, Writer w) { 
        int c; 
        while (c=r.read() != EOF) 
            w.write(c); 
    } 
}

也许这个例子你还没有清楚的了解抽象之后有什么好处,再看下面一个例子:
这里写图片描述

改进之后,就能够将finder变成任意的EmployeeFinder,例如XmEmployeeFinder, DBEmployeeFinder, FlatFileEmployeeFinder, MockEmployeeFinder….
而在之前的代码中,就无法这么方便的改变类的属性了。

接口聚合原则

接口聚合原则:客户端不应依赖于它们不需要的方法。

“胖”接口具有很多缺点。胖接口可分解为多个小的接口;不同的接口向不同的客户端提供服务;客户端只访问自己所需要的端口。下图展示出了这种思想。

这里写图片描述

ISP的一个反例:
这里写图片描述

OO设计原则:GRASP

GRASP是关于如何为“类”和“对象”指派“职责”的一系列原则。
对象的责任:与对象的义务相关
了解:

  • 了解私有封装数据
  • 了解相关对象
  • 了解它可以派生或计算的事物

执行:

  • 自己做某件事,例如创建对象或执行计算
  • 在其他对象中启动动作
  • 控制和协调其他对象中的活动。

看下面一个例子:
这里写图片描述
责任是使用方法实现的:makePayment意味着Sale对象有责任创建Payment对象。

GRASP由以下部分组成:

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

这一部分主要是自学,可以在以下链接继续学习。
CMU course

软件的可维护性问题知识与分析

前言        很多包括自己在内的开发人员都会经常去借用(我们不用剽窃这个词了!呵呵)开源代码进行二次开发;或者在前辈的遗留代码下,继续修修补补。这种经历往往并不像看起来那么简单——有时看懂,进...
  • huwei2003
  • huwei2003
  • 2014-08-22 16:28:48
  • 4656

衡量软件可维护性的七个特性

一.可理解性:可理解性表明人们通过阅读源代码和相关文档,了解程序功能及其如何运行的容易程度。 二.可靠性:可靠性表明一个程序按照用户的要求和设计目标,在给定的一段时间内正确执行的概率。 三.可测试性可...
  • lidoublewen
  • lidoublewen
  • 2009-05-12 13:45:00
  • 2535

软件的可维护性与可复用性

我们常说一个好的系统设计在于其有较高的可维护性和较高的可复用性。其实可维护性与可复用性是两个独立的目标,并不总是方向一致。         软件的维护就是软件的再生。一个好的软件设计,必须能够允许新的...
  • zsh2050
  • zsh2050
  • 2015-01-10 16:01:27
  • 1704

软件构造课程 Blog-1

软件构造课程 Blog-1 写在最开始  没有认为我也会有一天来更新自己的blog,那么既然机会,正好开始学习像Git记录代码的成长过程一样记录自己的一个变化,希望将来一天Blog+Githu...
  • qq_37487121
  • qq_37487121
  • 2018-03-07 17:04:21
  • 234

构造类型及结构体

1)构造类型及结构体         a、c语言构造类型 构造数据类型:构造数据类型是根据已定义的一个或多个数据类型用构造的方法来定义的。也就是说,一个构造类型的值可以分解成若干个“成员”或“元素”。...
  • ITclody
  • ITclody
  • 2015-06-26 01:03:10
  • 1449

机器学习算法_第1篇

决策树 介绍决策树基本思想:以信息熵为度量构造一颗熵值下降最快的树,到叶子节点处的熵值为0,此时每个叶子节点的实例都属于同一类。它是一种自顶向下的递归方法。 优点易于理解和实现; 数据的准备往往简单或...
  • yzhang6_10
  • yzhang6_10
  • 2016-05-28 14:28:08
  • 629

Java软件构造©Day1

Java软件构造©Day1 Day1 26th, February 标签(空格分隔): Java程序构造 Java软件构造©Day1 概况 基本信息 学期进程 第一章 三个角度看待程...
  • weixin_38338161
  • weixin_38338161
  • 2018-02-26 22:35:23
  • 126

《深入理解软件构造系统:原理与最佳实践》迷你书

  • 2012年06月14日 17:36
  • 3.02MB
  • 下载

软件构造系列学习笔记(1.1)——— 软件构造的多维视图

软件构建的多维视图 前记 构成软件系统的三个维度 软件构造的八个多维视图 视图之间的转变 总结 前记 这篇博客是软件构造系列学习笔记的第一篇,也是我博客的第一篇。开通博客主要也是因为...
  • fundament
  • fundament
  • 2018-03-06 22:57:42
  • 89

软件构造系列学习笔记(1.2)————软件构造的质量目标

软件构造的质量目标 软件系统的质量属性 软件构造的五个关键质量目标 软件系统的质量属性 软件系统的质量属性又分为两部分,外部质量因素(External quality factors)以及内部质量...
  • fundament
  • fundament
  • 2018-03-12 22:55:32
  • 21
收藏助手
不良信息举报
您举报文章:软件构造系列学习笔记(6.1)————可维护性的度量和构造原则
举报原因:
原因补充:

(最多只允许输入30个字)