软件构造-10 面向可维护性的构造技术
作者:互联网
本章面向可维护性:软件发生变化时,是否可以以很小的代价适应变化?
(本章偏理论,主要为记忆性内容,本博客仅为简单的记录与整理)
1.软件可维护性及其演化
- 软件可维护性类型: 纠错性(25%),适应性(21%),完善性(50%),预防性(4%)
- 软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了/在设计与开发阶段就要考虑将来的可维护性/设计方案要“easy to change”
2.可维护性指标
1.可维护性别名:可维护性,可扩展性,灵活性,可适应性,可管理性,支持性
2.code review时关于可维护性经常会问的问题:
设计结构是否足够简单
模块之间是否松散耦合
模块内部是否高度聚合
是否使用非常深的继承树
是否使用delegation替代继承
代码的圈/环复杂度是否太高
是否存在重复代码
3.常用可维护性度量:
1)圈/环复杂度:独立路径的数量 CC=number of areas
2)代码行数
3)Halstead Volume:多少独立算符
4)可维护性指数(MI):
5)继承的层次数
6)类之间的耦合度(尽量减少非常复杂的相互调用)
7)单元测试的覆盖度
3. 模块化设计和模块化原则
模块化编程:高内聚,低耦合(多模块之间关联较小,防止一个模块的大幅度改变影响另一个模块),分离关注点,信息隐藏
(1) 评估模块化的五个标准:
- 可分解性:能否容易分解成各个可独立解决的子问题,是模块之间的依赖关系显式化和最小化
- 可组合性:能够容易将多个模块组合起来形成新的系统,使模块在不同的环境下复用
- 可理解性:每个部分都可以被独立的理解
- 可持续性:发生变化时受影响范围最小
- 出现异常之后的保护:出现异常后受影响范围最小
(2) 模块化设计的五个原则:
- 直接映射:模块的结构与现实世界中问题领域的结构保持一致-》持续性,可分解性
- 尽可能少的接口:模块应尽可能少的与其他模块通讯-》可持续性、保护性、可理解性、可组合性
- 尽可能小的接口:如果两个模块通讯,那么它们应交换尽可能 少的信息-》可持续性,保护性
- 显式接口:当A与B通讯时,应明显的发 生在A与B的接口之间-》可分解性、可组合性、可持续性、 可理解性
5.信息隐藏:经常可能发生变化的设计决策应 尽可能隐藏在抽象接口后面
(3)耦合和内聚:
1.耦合:模块间的接口数目,每个接口的复杂度
2.内聚:模块内部的方法联系是否紧密
4. 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)责任:变化的原因
3)不应有多于1个的原因使得一个类发生变化
4)包含多个责任会导致:– 引入额外的包,占据资源
– 导致频繁的重新配置、部署等
5)一个类,一个责任
(2)OCP:(面向变化的)开放/封闭原则
1)对扩展性的开放:模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化
2)对修改的封闭:但模块自身的代码是不应被修改的/扩展模块行为的一般途径是修改模块的内部实现/如果一个模块不能被修改,那么它通常被认为是具有固定的行为
3)关键解决方案:抽象技术(尽量少用具体的)
(3)LSP:Liskov替换原则
子类型必须能够替换其基类型/派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异
(4)ISP:接口隔离原则
1)不能强迫客户端依赖于它们不需要的接口:只提供必需的接口
2)避免接口污染/避免胖接口
(5)DIP:依赖转置原则
1)高层模块不应该依赖于低层 模块,二者都应该依赖于抽象
2)抽象不应该依赖于实现细节,实现细节应该依赖于抽象
OO设计的两大武器:抽象(LSP,DIP,OCP)/分离(SPR,ISP)
5. 语法驱动的构造
有一类应用,从外部读取文本数据,在应用中做进一步处理
正则表达式:根据语法,开发一个它的解析器,用于后续的解析(不变的直接用‘’里的字符表示,其余可变的用符号组合表示,中间有比较复杂的模块就进行命名,单独脱离出来也写成一个表达式,比如下面eg中的hostname)
(1)基本操作:
– Concatenation 连接: x ::= y z (x匹配y与z连接)
– Repetition 重复: x ::= y* (x匹配0或更多个y)(自动机里的克林闭包?)
– Union 选择 : x ::= y | z (x匹配y或者z)
eg:
(2)更多操作:
1)选择 (出现0或1次),用 ? 表示 :
x ::= y? (x匹配y或者空)
2)出现1次或更多次 ,用 + 表示:
x ::= y+ (x匹配一个或更多y,与 x ::= y y*相等 )
3)字符类 [...],表示长度为 1 的字符串,其中包含任何一个方框中列出的字符:
x ::= [a-c] 等同于 x ::= 'a' | 'b' | 'c'
x ::= [aeiou] 等同于 x ::= 'a' | 'e' | 'i' | 'o' | 'u'
4) 倒排字符类 [^...],表示长度为 1 的字符串,其中包含括号中未列出的任何字符:
x ::= [^a-c] 等同于 x ::= 'd' | 'e' | 'f' | ...
(3)优先级:* ? +优先级最高,连接次之,| 最低
(4)在Java中使用正则表达式:
1)使用java.util.regex包中的三个类:
1>Pattern:Pattern对象是对regex正则表达式进行编译之后得到的结果
2>Matcher: 利用Pattern对输入字符串进行解析
3>PatternSyntaxException:object 是一个未经检查的异常,表示正则表达式中的语法错误
2)eg:
(5)字符类:
(6)预定义字符类:
(7)量词:
标签:10,原则,字符,接口,可维护性,模块,构造,Principle 来源: https://www.cnblogs.com/redTide/p/16335928.html