软件构造(8)- 可维护性与健壮性

可维护性的软件构造技术

一.可维护性的常见度量指标:
  1. 圈复杂度:圈复杂度大说明程序代码可能质量低且难于测试和维护
  2. 代码行数
  3. 可维护性指数(MI):0-100 利用公式计算
  4. 继承的层次数:层次越多越不好维护。CRP原则,尽量使用代理而不是继承。
  5. 类之间的耦合度
  6. 单元测试的覆盖度
二.聚合度与耦合度:

1.耦合度:衡量两个模块间的依赖关系,即其中一个模块的变化是否影响另一个
影响因素:模块间接口的数量和每个接口的复杂程度
2.内聚:衡量模块内的方法或属性的关联关系的强弱
模块之间联系越紧密,其耦合性就越强,模块之间独立性则越差
追求高内聚低耦合

三.OO设计原则:SOLID
  • (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 接口聚合原则
  1. SRP:不应该有多于1个原因让你的ADT发生变化,否则就拆分开
    否则会导致:1. 引入额外的包,占据资源 2. 导致频繁的重新配置、部署等

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

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

  4. ISP:接口隔离原则
    不能强迫客户端依赖于它们不需要的接口:只提供必需的接口
    将含有多个功能的接口分解为多个小接口,不同的接口向不同的客户端提供服务,客户端只访问自己所需要的端口

//bad example (polluted interface)
interface Worker {
	void work();
	void eat();
}
ManWorker implements Worker {
	void work() {…};
	void eat() {…};
}
RobotWorker implements Worker {
	void work() {…};
	void eat() {//Not Appliciable for a RobotWorker};
}
//Solution: split into two
interface Workable {
	public void work();
}
interface Feedable{
	public void eat();
}
//只需实现对应的接口即可
  1. DIP:依赖转置原则
    delegation的时候,要通过interface建立联系,而非具体子类

健壮性

健壮性:系统在不正常输入或不正常外部环境下仍能够表现正常的程度
正确性:程序按照spec加以执行的能力,是最重要的质量指标!

健壮性:尽可能保持软件运行而不是总是退出
正确性:永不给用户错误的结果

健壮性与正确性的比较

正确性倾向于直接报错(error),健壮性则倾向于容错(fault-tolerance)
健壮性:
让用户变得更容易:出错也可以容忍,程序内部已有容错机制
正确性:
让开发者变得更容易:用户输入错误,直接结束。(不满足precondition的调用)

对外的接口,倾向于健壮;对内的实现,倾向于正确

错误与异常

在这里插入图片描述
Error:程序员通常无能为力,一旦发生,想办法让程序优雅的结束
发生的原因:用户输入错误,设备错误,物理限制
Exception:程序的问题,可以捕获处理

异常处理

异常:程序执行中的非正常事件,程序无法再按预想的流程执行
将错误信息传递给上层调用者,并报告“案发现场”的信息
return之外的第二种退出途径

异常的种类

  1. 运行时异常:由程序员在代码里处理不当造成
    程序源代码中引入的故障所造成的 ;例如:ArrayIndexOutOfBoundsException, NullPointerException
    如果在代码中提前进行验证,这些故障就可以避免
  2. 其他异常:由外部原因造成
    程序员无法完全控制的外在问题所导致的;例如:FIleNotFoundException
    即使在代码中提前加以验证(文件是否存在),也无法完全避免失效发生。

Checked exceptions:
需要从Exception派生出子类型
必须捕获并指定错误处理器handler,否则编译无法通过,类似于静态类型检查
在这里插入图片描述
Unchecked exceptions:
从RuntimeException派生出子类型
可以不处理,编译没问题,但执行时出现就导致程序失败,代表程序中的潜在bug,类似于动态类型检查。
也可捕获,但不需要----相当于已知代码中的错误但不进行修改
eg:ArrayIndexOutOfBoundsException,NullPointerException
在这里插入图片描述
异常处理中的关键字:
Declaring exceptions (throws) 声明“本方法可能会发生XX异常” —在spec中使用
Throwing an exception (throw) 抛出XX异常
Catching an exception (try, catch, finally) 捕获并处理XX异常

Checked exceptions和Unchecked exceptions的区别
在这里插入图片描述
子类型异常处理规范:(LSP)

  1. 如果子类型中override了父类型中的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型更宽泛
  2. 子类型方法可以抛出更具体的异常,也可以不抛出任何异常
  3. 如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常。

finally语句:
当异常抛出时,方法中正常执行的代码被终止
如果异常发生前曾申请过某些资源,那么异常发生后这些资源要被恰当的清理
try-catch-finally结构

  1. 当try中代码不抛出异常时
    不管程序是否碰到异常,finally都会被执行
  2. try中代码抛出异常被catch捕获
    处理结束后仍会执行finally

在这里插入图片描述
但如果catch块中出现throws异常 上图只会执行1 3 5

  1. try中代码抛出异常但未被catch捕获
    出现异常后,try中剩余的语句会被跳过,然后finally执行,并将异常返回给调用者
    eg:如下程序只执行1.5
    在这里插入图片描述
断言与防御式编程

避免引入bug

  1. 静态类型检查
  2. 动态类型检查
  3. 不可变性
  4. 不可变的值:final修饰
  5. 不可变引用:final修饰

限制bug作用范围

  1. 限定在一个方法内部,不扩散
  2. fail fast:尽快失败,就容易发现、越早修复
    Pre-condition如果违反,该方法可以做任何事
    应该尽可能早的指出client的bug

断言Assertions
断言:在开发阶段的代码中嵌入,检验某些“假设”是否成立。若成立,表明程序运行正常,否则表明存在错误。出现AssertionError,意味着内部某些假设被违反了
使用断言的主要目的是为了在开发阶段调试程序、尽快避免错误
eg:检测pre-condition 是否成立

assertion使用规范:

assert condition ;
//所构造的message在发生错误时显示给用户,便于快速发现错误所在
assert condition : message;

可检测的情况:

  1. 内部不变量:assert x>0;
  2. 表示不变量:checkRep();
  3. 控制流不变量:在控制流不应该到达的位置添加断言 eg:switch-case中的default
  4. 方法的前置条件
  5. 方法的后置条件

不可使用的情况:

// don't do this: 
x = y + 1; 
assert x == y+1;

// don't do this: 因为代码中的assert可被disable,需要执行的程序结构不能在assert中
assert list.remove(x);
// do this: 
boolean found = list.remove(x); 
assert found;

断言非常影响运行时的性能,可使用-da进行disable

断言与异常的比较

使用异常来处理你“预料到可以发生”的不正常情况
使用断言处理“绝不应该发生”的情况

如果参数来自于外部(不受自己控制),使用异常处理
如果来自于自己所写的其他代码,可以使用断言来帮助发现错误,检测非public类中的前置条件和所有方法中的postcondition

断言和异常处理都可以处理同样的错误
开发阶段用断言尽可能消除bugs,在发行版本里用异常处理机制处理漏掉的错误

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章