软件维护和演变
可维护性度量
模块化设计和模块化原则
OO设计原则:SOLID
OO设计原则:GRASP
总结程序员
什么是软件维护?编程
软件工程中的软件维护是交付后修改软件产品以纠正故障,提升性能或其余属性。软件维护:修复错误,改善性能
在“ISO / IEC 14764:2006软件工程 - 软件生命周期过程 - 维护”设计模式
运维工程师安全
维护是软件生产中最困难的方面之一,由于维护包含了全部其余阶段的各个方面
用户报告故障并由维护工程师处理。网络
维护工程师必须具有出色的调试技能app
软件维护的类型运维
纠正性维修25%纠错性编辑器
适应性维护21%适应性模块化
完善性维护50%完善性函数
预防性维护4%预防性
雷曼关于软件演化的规律(Lehman’s Laws on Software Evolution)
反馈系统
持续变化
持续增加
质量降低
增长复杂性
自我调节
软件维护和进化的目标
软件维护和演化的目标:为了提升软件的适应性和适应性,并保持其活力,即“长期软件(低熵软件)”
提升软件的适应性,延续软件生命
Linux内核发展的一个例子:可维护性指数
维护不只仅是op工程师的任务......
维护不只仅是维护和操做工程师的任务,也是软件设计人员和开发人员的潜在任务。 软件维护不只仅是运维工程师的工做,而是从设计和开发阶段就开始了
对他们来讲,在设计和施工阶段必须考虑软件的将来潜在变化/扩展;在设计与开发阶段就要考虑未来的可维护性
所以,灵活和可扩展的设计/结构被全面考虑,换句话说,“易于改变/扩展”。 设计方案的“容易改变”
这就是所谓的软件构建的“可维护性”,“可扩展性”和“灵活性”。
可维护性建设的例子
模块化设计与实现模块化
OO设计原则OO设计原则
OO设计模式OO设计模式
基于状态的构造技术(自动机编程)
表驱动的构造技术
基于语法的构造技术(Grammar-based construction)
许多可维护性的名字
可维护性
可扩展性
灵活性
可适应性
可管理性
可支持性
有关可维护性的问题
Code review的时候常常问的关于可维护性的问题:
一些经常使用的可维护性度量标准
圈复杂度 - 测量代码的结构复杂度。
Lines of Code(代码行数) - 表示代码中的近似行数。
对于给定的问题,令:
η_1=不一样运算符的数目
η_2=不一样的操做数的数量
Ν_1=运算符总数
Ν_2=操做数总数
从这些数字中能够计算出几项度量:
程序词汇表:η=η_1+η_2
程序长度:N=Ν_1+Ν_2
计算的程序长度:N ̂=η_1 log_2〖η_1 〗+η_2 log_2〖η_2 〗
体积:V=N×log_2η
难度:D=η_1/2×Ν_2/η_2
代价:E=D×V
难度测量与程序编写或理解的难度有关
代价度量使用如下关系转化为实际编码时间,
编程所需的时间:T=E/18 秒
Halstead提供的错误(B)是对执行错误数量的估计。
提供的错误数量:B = E^(2/3)/3000或更接近的B = V/3000也被接受。
Halstead Volume:基于源代码中(不一样)运算符和操做数的数量的合成度量。
一些经常使用的可维护性度量标准
可维护性指数(MI)- 计算介于0和100之间的索引值,表示维护代码的相对容易性。 高价值意味着更好的可维护性。 它的计算基于:
一些经常使用的可维护性度量标准
继承的层次数
类之间的耦合度
单元测试覆盖率
模块化编程
模块化编程是一种强调将程序功能分离为独立,可互换模块的设计技术,每种模块都包含执行所需功能一个方面所需的一切。
将整个程序的代码高度分解为结构化编程和OOP。
模块化编程设计的目标是将系统划分为模块,并经过如下方式在组件之间分配责任:
模块化下降了程序员在任什么时候候都必须处理的整体复杂性,假设:
内聚和耦合的原则多是评估设计可维护性的最重要的设计原则。
(1)评估模块性的五个标准
Decomposability(可分解性)
Composability(可组合性)
Understandability(可理解性)
Continuity(可持续性)
Protection(出现异常以后的保护)
(2)模块化设计的五条原则
Direct Mapping (直接映射)
Few Interfaces (尽量少的接口)
Small Interfaces (尽量小的接口)
Explicit Interfaces (显式接口)
Information Hiding (信息隐藏)
(3)耦合和内聚
耦合
耦合是模块之间依赖关系的度量。 若是两个模块之间的变化可能须要另外一个模块的变动,则两个模块之间存在依赖关系。
模块之间的耦合度取决于:
HTML,CSS和JavaScript之间的耦合
一个精心设计的网络应用程序模块化:
内聚
内聚是衡量一个模块的功能或责任有多强烈程度的一个指标。
若是一个模块的全部元素都朝着相同的目标努力,那么它就具备很高的内聚。
最好的设计在模块内具备高内聚力(也称为强内聚力)和模块之间的低耦合(也称为弱耦合)。
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:
若是一个类包含了多个责任,那么将引发不良后果:
SRP是原则中最简单的一种,也是最难作到的一种。(最简单的原则,倒是最难作好的原则)
类应扩展(对扩展性的开放)
但关闭修改。(对修改的封闭)
Key:abstraction(关键的解决方案:抽象技术)
“软件实体(类,模块,函数等)应该被打开以进行扩展,可是为了修改而关闭”,即,使用继承和组合来改变类的行为
开放封闭原则 - 几个问题...。
不可能在不修改GraphEditor的状况下添加新的Shape
重要的是要了解GraphEditor添加一个新的形状
GraphEditor和Shape之间的紧密耦合
不参与GraphEditor就很难测试特定的Shape
If-Else-/Case应该被避免
OCP表示单一选择
只要软件系统必须支持一组替代方案,系统中的一个且只有一个模块应该知道他们的详尽列表。
编辑器:一组命令(插入,删除等)
图形系统:图形类型集(矩形,圆形等)
编译器:语言结构集(指令,循环,表达式等)
“使用指针或对基类的引用的函数必须可以使用派生类的对象而不知道它”,即,子类在使用它们的基类时应该表现得很好
LSP:子类型必须可替代其基本类型。(子类型必须可以替换其基类型)
派生类必须能够经过基类接口使用,而不须要客户端了解其差别。 (派生类必须可以经过其基类的接口使用,客户端无需了解两者之间的差别)
在第5-2节中已经讨论过可复用性。
“客户不该该被迫依赖他们不使用的接口”,即保持接口小。
不要强制类来实现它们不能实现的方法(Swing / Java)
不要用不少方法污染界面
避免“胖”的接口
客户不该该被迫依赖他们不使用的方法。(客户端不该依赖于它们不须要的方法)
接口属于客户端,而不属于层次结构。
这个原则处理“胖”接口的缺点。(“胖”接口具备不少缺点)
具备“胖”接口的类是接口不具备内聚性的类。(不够聚合)
高级模块不该该依赖于低级模块。 二者都应该取决于抽象。
应该使用大量的接口和抽象!
为何DIP?
优势:
尝试设计测试
例如:你会避免循环依赖。 若是您必须从UI单独测试,业务逻辑将更好地与UI代码隔离
什么是GRASP模式
通常责任分配软件模式(原则),缩写为GRASP,包含为OOP中的类和对象分配责任的准则。
GRASP模式是帮助理解基本对象设计的学习辅助,并以有条理,合理,可解释的方式应用设计推理。
这种理解和使用设计原则的方法是基于对班级分配责任的模式。
GRASP是关于如何为“类”和“对象”指派“职责”的一系列原则
什么是责任
对象的责任:与对象的义务有关
了解:
这样作:
软件维护和演变可维护性度量模块化设计和模块化原则OO设计原则:SOLIDOO设计原则:GRASP