软件构造基础
a.软件的多维视图
b.内部/外部的质量指标
1、软件系统的质量特性
外部和内部的质量因素
外部质量是用户能够感受到的,影响用户使用的,例如速度、易用性。
内部质量影响使用代码的相关人员,影响软件本身和开发者。
对于用户来讲,尽管只有外部因素重要,但实现外部因素的关键是实现内部因素。
(1)外部质量因素
正确性
正确性是软件产品能够正确的执行任务的能力,是首要的质量目标。保证正确性的途径是有条件的。
一个软件系统设计多个层面,所以不可能仅仅靠每个构件和属性的正确性来保证整体的正确性,各层之间是相互依赖的。
假定一个软件系统是分层开发的,每层都要确保自己是正确的,同时假定其调用的低层也是正确的。
保证正确性的途径:测试和Debug
像类型化/静态类型的检查(type、assert)的防御性编程以防患于未然
正式方法:数学方法验证(涉及到软件检验相关的研究生课程与科研领域)
健壮性
健壮性是软件系统对异常情况做出适当反应的能力。
稳健性补充了正确性。
正确性解决了系统在其规范所涵盖的情况下的行为;
健壮性描述了在该规范之外发生的事情。
健壮性是确保如果出现此类情况,系统不会导致灾难性事件;它应生成适当的错误消息,干净地终止执行,或进入所谓的“优雅降级”模式。
健壮性同异常情况相关,异常或非异常取决于程序的规格说明,异常情况是程序的规格说明中没有涉及的部分。
判断是否异常是异常的标准,不取决于客观的正确标准,而取决于程序的规格说明。
可扩展性
可扩展性是指软件易于调整以适应变化的能力。可扩展性同规模密切相关,越大越难以扩展。
软件是易变的,需要可扩展性。
传统方法最初冻结需求,假设没有变化,因此难以适应变化
两个提高可扩展性的原则:
简单的体系结构总是比复杂的体系更容易适应变化。
模块自治性越强,变化时对其余模块影响 越小。
(这几个部分课件上都翻译好了,无耻的照抄过了
重用性
软件经常遇到相似的模式,利用共性,避免重复实现。
(课件里一些部分有些重复,也不是特别关键,就不放上来了
兼容性
兼容性是指软件元素易于与其他的组合。因为我们的软件并非运行在真空中,但难点在于不同的软件有不同的设定。所以,解决兼容性的关键是标准性,例如规定文件格式、数据结构、用户接口等。更通用的方法是通过协议来实现更通用的兼容性。
效率
效率是软件系统对硬件资源尽可能少的需求的能力,但前提是软件的正确性,并且需要与其他目标进行权衡。
可移植性
可移植性是指便于将软件产品转移到各种硬件和软件环境。
易用性
用户可以轻松掌握软件的使用,也包括安装、运行和监控的容易度 。既方便初学者入门,也不影响熟练的用户直接使用。一个关键是结构要简洁,理解用户,换位思考,站在用户的角度设计软件。
功能性
增加功能的同时也要防止陷入“蠕变特性”,即程序设计中一种不 适宜的趋势,即软件开发者增加越来越多的功能,企图跟上竞争,其 结果是程序极为复杂、不灵活、占用过多的磁盘空间 。过多的新功能容易带来一 致性的缺失,影响易用性 。
在质量提升技术的帮助下,可以在整个项目中保持质量水平不变,而不仅仅 是功能性。
通常设计时,首先实现一小部分功能并保证各种质量目标,逐渐增加功能并继续保证质量目标。
及时性
及时性是指软件系统能够在用户需要时或者需要需要之前发布。
其他质量目标
可验证性:是否易于验证
完整性:软件系统保护其各种组件(程序和数据)免受未经授权的 访问和修改的能力。
可修复性:便于修复软件的缺点
经济性:同及时性相伴,是系统能够按照其分配的预算或低于预算完成的能力。
(2)内部质量目标
圈复杂度:用来衡量一个模块判定结构的复杂程度 。
耦合度:设计中追求高内聚和低耦合。
可读性、易理解性、清晰度、复杂度、体积
内部质量因素通常用作外部质量因素的部分度量。
软件配置管理(SCM)和版本控制系统(VCS)
软件配置管理(SCM)
SCM的任务是追踪与控制软件中的变化,SCM实际任务是版本控制和建立基线。
版本控制系统(VCS)
本地版本控制系统
中心式版本控制系统
分布式版本控制系统
版本控制系统的特性
可靠的、多文件、有意义的版本、回退、比较版本、查看历史等。
融合、责任跟踪、以及允许一个程序员独自工作一段时间,允许多个程序员共享未完成的工作。
软件构造过程
软件构造的一般过程:设计→编程/重构→Debug→测试→构建→发布
软件构造的狭义过程(构建):验证→编译→链接→测试→打包→安装→部署
ADT+OOP
类型与变量
类型是一组值,以及可以对这些值执行的操作。在Java中分为基本数据类型和对象数据,通常基本数据类型小写开始,对象数据类型大写开始。
对于基本数据类型,按照值来区分,而对象数据类型则不是,即使值相同也未必相等,就像String不能用双等号,而需要用.equal来判断是否相等。
对象类型的层级关系
基本数据类型的包装类
Boolean, Integer, Short, Long, Character, Float, Double
实际上就是把基本数据类型封装为对象,但尽可能少用。另外在使用一些像List这样类时,不允许List,需要List list = new ArrayList();
在这个过程,list.add(1)会被自动转换为list.add(Integer.valueOf(1)),会降低效率。
操作
重载运算符和操作
(重载在之后的章节会具体讨论
可变性与不可变性
改变变量和改变它的值
简单的说前者是改变变量的指向,比如test = new ArraryList()就是将这个类指向了堆上新的位置,而test.add(“Good”)就是在修改它的值。用课件上的话来说,“对变量赋值是在改变变 量的指向,指向不同的值 ”,“改变可变变量的内容时 ,是在改变变量内部内容的引用 ”。
不变性
final关键字对不可变引用提供了静态检查的支持,建议利用final来声明方法的参数和局部变量。
final class表示它不能被继承,final method表示它不能被重写。
**不可变的对象:**一旦创建,总是表示相同值。
可变的对象:它们的方法可以修改对象的值。
抽象类型操作的分类
Creators:产生类型的新对象
Producer:在已有对象基础上产生新的对象
Observer:输入抽象类型的对象,返回其他类型的对象
Mutators:改变对象
行为子类型和里氏代换原则(LSP)
Java会确保以下规则:
子类型可以增加方法,但不可删
子类型需要实现抽象类型中的所有未实现方法
子类型中重写的方法必须有相同或子类型的返回值
子类型中重写的方法必须使用同样类型的参数
子类型中重写的方法不能抛出额外的异常
或者说:更强的不变量(RI)、更弱的前置条件和更强的后置条件
里氏替换原则(LSP)
LSP是行为子类型的一个特殊定义,称作强行为子类型化
要求:
前置条件不能强化
后置条件不能弱化
不变量要保持
子类型方法参数:逆变
子类型方法返回值:协变