第六章--第一节:可维护性的度量和构造原则

第六章:面向软件构造的可维护性的构建方法

第一节:可维护性的度量和构造原则

问题一:什么是软件的维护和演化

  •     维护的含义:软件工程中的软件维护是交付后修改软件产品以纠正故障,提高性能或其他属性,简而言之,软件维护:修复错误、改善性能。
  •     维护的类型:纠错性(25%)、适应性(25%)、完善性(50%)、预防性(4%)
  •     演化的含义:对软件进行持续的更新
  •     软件演化的规律(Lehman):软件质量下降,复杂度增加。 
  •     软件维护和演化的目标:提高软件的适应性,延续软件生命 。
  •     意义:软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了 。在设计与开发阶段就要考虑将来的可维护性 ,设计方案需要“easy to change”。
  •     体现可维护性建设的例子:模块化、OO设计原则(– SOLID、GRASP)、OO设计模式(Factory method pattern, Builder pattern、Bridge pattern, Proxy pattern、Memento pattern, State pattern)、基于状态的构造技术、表驱动的构造技术、基于语法的构造技术

问题二:可维护性(Maintainability)的度量

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

    可维护性的别名:可扩展性(Extensibility)、灵活性(Flexibility)、可适应性(Adaptability)、可管理性(Manageability)、支持性(Supportability)。

       可维护性的度量标准:

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

问题三:模块化设计和模块化原则

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

    设计目标:

  • 模块内高内聚
  • 模块间低耦合
  • 分离关注点
  • 信息隐藏

    (模块化降低了程序员在任何时候都必须处理的总体复杂性,因为模块化实现了分离关注点和信息隐藏)(内聚和耦合的原则可能是评估可维护性的最重要的设计原则)

     2.评估模块化的五个标准

  • 可分解性:将问题分解为各个可独立解决的子问题
  • 可组合性:可容易的将模块组合起来形成新的系统
  • 可理解性:每个子模块都可被系统设计者容易的理解
  • 可持续性:小的变化将只影响一小部分模块,而不会影响整个体系结构
  • 出现异常之后的保护:运行时的不正常将局限于小范围模块内

    3.模块化设计的五条原则

  • 直接映射:模块的结构与现实世界中问题领域的结构保持一致
  • 尽可能少的接口:模块应尽可能少的与其他模块通讯
  • 尽可能小的接口:如果两个模块通讯,那么它们应交换尽可能少的信息
  • 显式接口:当A与B通讯时,应明显的发生在A与B的接口之间
  • 信息隐藏:经常可能发生变化的设计决策应尽可能隐藏在抽象接口后面

问题四:OO设计原则:SOLID

    SOLID:

  1. (SRP) The Single Responsibility Principle 单一责任原则

        含义:不应有多于1个的原因使得一个类发生变化;一个类,一个责任。(如果一个类包含了多个责任,那么将引起不良后果:引入额外的包,占据资源;导致频繁的重新配置、部署等。)

        反例:

第六章--第一节:可维护性的度量和构造原则_第1张图片

    2.(OCP) The Open-Closed Principle 开放-封闭原则

        含义:开发指的是对扩展性的开放,模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化。封闭指的是对修改的封闭,模块自身的代码是不应被修改的,扩展模块行为的一般途径是修改模块的内部实现,如果一个模块不能被修改,那么它通常被认为是具有固定的行为。

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

        反例:

第六章--第一节:可维护性的度量和构造原则_第2张图片

        *例:

// 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; } } 

    上面代码存在的问题:

    1.不可能在不修改GraphEditor的情况下添加新的Shape

    2.GraphEditor和Shape之间的紧密耦合

    3.不调用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 } } 

    3.(LSP) The Liskov Substitution Principle Liskov替换原则

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

        (这条原则在第五章第二节中已经详细将结果,在此不再赘述)

    4.(DIP) The Dependency Inversion Principle 依赖转置原则

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

        *例:

        第六章--第一节:可维护性的度量和构造原则_第3张图片

    改进之前的代码:

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); 
    } 
}
    **例:

    第六章--第一节:可维护性的度量和构造原则_第4张图片

(改进之后,就能够将finder变成任意的EmployeeFinder,例如XmEmployeeFinder,DBEmployeeFinder,FlatFileEmployeeFinder, MockEmployeeFinder…. 

而在之前的代码中,就无法这么方便的改变类的属性了。)

    5.(ISP) The Interface Segregation Principle 接口聚合原则

        含义:客户端不应依赖于它们不需要的方法。

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

        第六章--第一节:可维护性的度量和构造原则_第5张图片

        反例:

        第六章--第一节:可维护性的度量和构造原则_第6张图片

问题五:OO设计原则:GRASP

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

    组成:

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





你可能感兴趣的:(软件构造学习)