软件维护——修复错误、改善性能。
运维是软件开发中最困难的工作之一。
处理来自用户报告的故障/问题。
修复代码后——测试所作的修改;回归测试;记录变化。
除了修复问题,修复中不能引入新的故障。
最大的问题:修改后没有足够的文档记录和测试。
软件维护的类型:纠错性、适应性、完善性、预防性。
软件演化——对软件进行持续的更新。
软件的大部分成本来自于维护阶段。
软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了。
在设计与开发阶段就要考虑将来的可维护性。
设计方案的"easy to change"。
可维护性的很多名字——可维护性;可扩展性;灵活性;可适应性。
可维护性的问题——设计结构是否简单;模块之间是否松散耦合;模块内部是否高度聚合;是否使用了非常深的继承树,是否使用了delegation替代继承;代码的圈复杂度是否太高;是否存在重复代码。
一些常用的可维护性度量指标——圈复杂度;代码行数;可维护性指数;继承的层次数;类之间的耦合度;单元测试的覆盖度。
模块化编程——高内聚、低耦合、分离关注点、信息隐藏。
内聚性——衡量一个模块内部各功能或职责之间相关性强度的指标。
高内聚——如果一个模块内的所有元素都在为实现同一个目标而工作,则该模块具有高内聚性。
S——SRP单一责任原则。
O——OCP开发——封闭原则。
L——LSP里氏替换原则。
D——DIP依赖转置原则。
I——ISP接口聚合原则。
不应该有多于一个原因让ADT发生变化,否则就拆开。
责任——变化的原因。
对扩展性的开放——模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化。
对修改的封闭——但模块自身代码不应被修改;扩展模块行为的一般途径是修改模块的内部实现;若一个模块不能被修改,则通常被认为有固定行为。
关键解决方案——抽象技术。
子类型必须能够替换其基类。
派生类必须能够通过其基类的接口使用,客户端无需了解二者间差异。
不能强迫客户端依赖于它们不需要的接口,只提供必需的接口。
客户端不应依赖于它们不需要的方法。
“胖”接口具有很多缺点,不够聚合。可分解为多个小接口,不同接口向不同客户端提供服务,客户端只访问自己所需要的端口。
抽象的模块不应依赖于具体的模块。
具体应依赖于抽象。
有一类应用,从外部读取文本数据,在应用中做进一步处理。
输入文件有特定格式,程序需读取文件并从中抽取正确内容。
从网络上传输过来的消息,遵循特定的协议。
用户在命令行输入的质量,遵循特定的格式。
内存中存储的字符串,也有格式需求。
使用grammar判断字符串是否合法,并解析成程序里使用的数据结构。
通常是递归的数据结构。
正则表达式。
根据语法,开发一个它的解析器,用于后续的解析。
终结符——语法中的字面字符串:
用语法定义一个“字符串”。
语法中的字面字符串 (literal strings)
被称为终结符(也叫终止节点或叶节点)
语法解析树的叶子节点;无法再往下扩展;通常表示为字符串。
语法中的非终结符与产生式:
语法描述:语法由一组产生式 (productions)
描述,每个产生式定义了一个非终结符 (nonterminal)。
遵循特定规则,利用操作符、终止节点和其他非终止节点,构造新的字符串。
产生式的形式——非终结符 ::= 由终结符、非终结符和操作符组成的表达式。
语法中的某一个非终结符会被指定为根节点 (root)。
基础语法操作符——连接、重复*、选择|。
使用括号对运算符进行分组。
更多语法运算符——可选运算符?、1次或多次运算符+、字符类[...]、取反字符类[^...]。
将语法与字符串进行匹配时,可以生成一棵解析树。这棵树展示了字符串的各个部分是如何与语法的各个部分相对应的。
解析树的特征——叶子节点由终结符标记,代表字符串中已被解析的部分;叶子节点无任何子节点,不可扩展;按顺序将所有叶子节点拼接,字符串还原。