大工软件工程复习(下)
第八章:设计优化
设计的异味
僵化性:隐藏的设计关联性,导致对文档或代码进行小的修正却埋下了不可预期的隐患,这将导致系统的改动困难重重。
脆弱性:在进行一个改动时,可能会导致程序许多没有概念关联的地方出现问题。
顽固性:分离设计中包含的有价值的部分并进行重用的付出和风险是巨大的。
粘滞性:在添加新功能时只是在现有代码的基础上拼凑代码,不愿意也不敢去触碰现有代码,不对代码重构,导致原有设计的破坏和退化。
不必要的复杂性:设计人员预测需求的变化,为过多的可能性做准备,致使设计含有了绝不会用到的内容,无法带来回报。
不必要的重复性:设计中含有重复的内容,而这些重复的内容本可以使用单一的抽象进行统一,导致修改无法保持一致。
晦涩性:模块难以阅读和理解,代码随着时间而演化,变得越来越晦涩,逐渐丧失清晰性和表达力。
设计原则的理解
设计原则有:接口隔离原则、依赖倒置原则、开放封闭原则、 Liskov替换原则、单一职责原则、合成/聚合复用原则。
接口隔离原则
接口隔离原则(The Interface Segregation Principle, ISP):
- 应尽量使用“接口继承” ,而非“实现继承”
- 通过接口只将需要的操作“暴露”给客户类,而将不需要的操作隐藏起来
好处:就是当业务需求变化时,改变的是具体类,这些变更通过稳定的抽象类进行隔离,使得Client不受变化的影响,从而提高了系统的可维护性。面向接口的设计能够使Client只需关注如何进行业务活动,而不必关心其使用的对象的具体实现。一个对象可以很容易地被(实现了相同接口的)另一个对象所替换,不必硬绑定(hard wire)到一个具体类的对象上,因此增加了灵活性。(如修车的人和驾驶人都调用车这个类的不同的部分功能)。
这是一种松散的耦合,同时增加了重用的可能性。
依赖倒置原则
依赖倒置原则(Dependency Inversion principle, DIP):
- 应依赖于抽象,而不要依赖具体
好处:则使细节和具体实现都依赖于抽象,抽象的稳定性决定了系统的稳定性。
开放封闭原则
开放封闭原则(The Open-Closed Principle, OCP):
- 一个模块对扩展应是开放的,而对修改应是封闭的
基本的特点:
- 模块的行为可以被扩展,以需要满足新的需求。
- 模块的源代码是不允许进行改动的。
这条原则是面向对象思想的最高境界,即设计者应给出对于需求变化进行扩展的模块,而永远不需要改写已经实现的内部代码或逻辑。OCP是相对的,不存在绝对符合OCP的设计,而且一个软件系统的所有模块不可能都满足OCP,要做的是尽量最小化不满足OCP的模块数量。
Liskov替换原则
Liskov替换原则(Liskov Substitution Principle, LSP):
- 任何出现父类的地方应能使用子类对其进行无条件的替换,即当使用子类对其父类进行替换时,该组件仍象替换前一样正常工作。
当子类型替换父类型后不能违反父类型中的前置条件和后置条件,即一个子类型不得具有比父类型更多的限制。
单一职责原则
单一职责原则(Single Responsibility Principle, SRP):
- 所谓职责,可理解为功能,就是设计的类功能应该只有一个,而不应为两个或多个。
职责是引起“变化”的原因:当一个类中有两个以上的变化方向,会产生过多的变化点。多个功能在一个类中是可以同时存在的,但这里有个前提:是否能够成为变化的方向。如果成为单独的变化方向,则应该按照SRP进行类职责的拆分,否则可以保留功能共存(装饰模式) 。
合成/聚合复用原则
合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP):
- 合成与聚合是两种特殊的关联关系,是以委托方式实现对象间功能的重用(另外一种面向对象特有的重用方式是继承)。
委托重用与继承重用是两种本质上不同的重用方式,委托重用追求的是对象间的独立性即低耦合,而继承重用追求的是对象间应能尽可能的高内聚。合成/聚合复用原则指的是应尽量使用合成/聚合形式的委托重用,尽量不使用继承重用。
当然,在满足下列条件的时候继承重用也是推荐的。
- 子类的一个实例永远不需要转化为其它类的一个对象
- 子类是对其父类的职责进行扩展,而非重写或废除,因为这会增加违反Liskov替换原则(LSP)的可能性。
设计模式
从高到低,模式分为3个层次:架构模式、设计模式和实现模式。
设计模式分为:
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂(抽象工厂)创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
单例模式
单例模式(Singleton)保证了一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式要求:
- 类的所有构造方法都为私有的,防止其被外部创建;
- 提供一个公有的方法获取该类的实例;
- 类中的实例变量为私有或受保护的。
适配器模式
适配器模式(Adapter)把一个类的接口变换成客户类所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。
适配器一般有两种工作方式:一种是通过委托的方式,另一种是通过继承(接口实现)的方式。
桥模式
桥模式(Bridge)的主要思想是将抽象部分与它的实现部分(行为)进行分离,使它们都可以独立地变化。
使用桥模式时,首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。
装饰模式
装饰模式(Decorator Pattern)以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。
动态给一个对象增加功能,并可以再动态的撤消,因此增加由一些基本功能的组合而产生的非常大量的功能。
装饰模式中既有继承又存在组合,实际上是将Bridge中的抽象和实现合二为一了,是其特殊形式。
门面模式
门面模式(Facade)要求外部与一个子系统的通信必须通过一个统一的门面对象进行。
门面模式提供了一个高层次的接口,使得子系统更易于使用。另外,每个子系统一般只要求具有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。
这个时候的门面类作用相当于前面介绍的适配器,负责对外部请求的转发,并且可以在此基础上进行功能的扩充,如对传递进来的参数的验证等。整个系统可以有多个门面类。
更为常见的做法是系统并不提供一个门面类,而是提供一个或多个门面接口。
门面类虽然具有多种功能,但它每次为外部提供服务的时候一般只涉及其中一类功能,几乎不会做各种功能的联合使用,也就是这些功能多独立变化,不会形成组合在一起形成的多个变化点,因此本质上并不违反单一职责原则的精神。
代理模式
代理模式(Proxy)一般用来对有价值(稀缺)资源的管理,比如数据库的连接等,目的就是为了提高这些资源的利用率或者
系统性能。
它给这些资源对象提供一个代理对象,并由代理对象控制对资源对象的使用,起到中介的作用。
代理对象的存在使得客户类分辨不出代理对象与真实的资源对象
观察者模式
观察者模式(Observer)定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象;
当这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。MVC架构模式在实现上就使用了观察者模式,其中的主题对象就相当于MVC中的模型,观察者对象相当于MVC中的视图。
策略模式
策略模式(Strategy)针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
策略模式的好处是能够使得算法可以在不影响到客户端的情况下发生变化,而且将算法的行为和环境分开,环境类负责维持和查询行为类,各种算法在具体的策略类中提供。
状态模式
状态模式(State)可以看作是策略模式的一种应用,状态模式允许一个对象在其内部状态改变的时候改变行为。
状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。
状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创建一个状态类的子类。当系统的状态变化时,系统便改变所选的子类,从而对类在不同状态下的行为进行管理。
第九章 实现技术
实现相关的关键技术:数据管理策略和方法、数据持久化、XML、领域特定语言(DSL)、模型驱动架构 (MDA)、重构(Refactoring)。
组件
组件可以理解为一种特殊的对象,组件是对数据 和方法的简单封装。
数据的持久化
数据的持久化存储一般有两类方式:物理文件或数据库系统。
数据库系统的好处在于不同的应用可以在同一时间对同一数据并发的使用,提供了很好的数据共享性和安全性。很多在分布系统中存在的大部分问题都可以通过数据库系统解决。
大工软件工程复习4
第十一章 软件测试
测试技术
测试分类:功能测试,非功能测试、界面测试。
功能测试:
- 白盒测试(方法和类测试)
- 灰盒测试(集成测试)
- 黑盒测试(系统测试)
测试策略:
上个迭代周期中的测试用例需要在本次的开发迭代测试中重新执行以确保对原有功能没有引入新的缺陷,这是一种回归测试。
软件测试流程
软件度量
McCabe指标:方法复杂程度的度量:
McCabe环形复杂度:边数 - 节点数 + 2
复杂度的计算过程的简化:
- 每个代码段初始复杂度为1
- 遇到每个原子条件加1
- 每个switch中的case段加1
如果复杂度大于10,应考虑将该方法简化,而类中的方法McCabe值一般限制在5以下
LCOM*指标:类的内聚性的度量:
m为方法数,a为所含的实例变量数,u(Aj) 为访问每个实例变量的方法数。当LCOM为0时,该类的内聚性最佳,越靠近0则内聚性越高。.由于get和set方法一般只对一个变量进行访问,为降低它们对LCOM的影响,在计算时可不考虑类中的set和get方法。‘
测试方法
等价类测试
对于数值型的集合,可根据输入变量的取值范围,产生一个有效等价类和两个无效等价类。
对于非数值类型的集合稍微复杂一些,比如对于枚举类型,假设合理的取值包括red、yellow和blue,则可以简单的将这些取值分别对应一个等价类。
如果不允许其它值作为输入数据,则不存在它的无效等价类,例如Enumeration类型就是这样的情况。否则可以将所有其它输入对应一个无效等价类。
例子:创建某学生对象的数据,具有名字、出生年份和专业3个属性。名字属性要求不能为空,出生年份要求介于1900和2000之间;专业取值只能是枚举类型,包括“贸易(TRADE)、计算机(CS)、数学(MATH)”中的一个元素。
对于输入数据可产生如下的等价类:
将3个变量具有的等价类分别进行组合,就会产生233=18个测试用例,这将涵盖所有可能的输入情况,把这种组合方式称为强等价类方法。
等价类测试方法关注被测对象向外界提供的功能,测试用例设计并不依赖程序内部结构,被称为是一种黑盒测试的方法。
基于控制流的测试
白盒测试的思想是充分利用程序的结构信息来设计测试用例,以实现对每个程序块代码的覆盖。覆盖程度和指标在等价类方法中通常无法保证和度量。
覆盖指标:语句覆盖、分支覆盖、条件覆盖、多条件组合覆盖、路径覆盖。
-
语句覆盖:语句覆盖表示在程序控制流图中测试经过的节点数与所有节点数的比例。
-
分支覆盖:分支覆盖的目标是尽可能覆盖控制流图中所有的边。
满足分支覆盖要求一定会满 足语句覆盖要求。
-
条件覆盖:要求每个原子谓词的真假两种取值都要取到。
条件覆盖与分支覆盖并没有直接的关系。
-
多条件组合覆盖:所有的覆盖要求在多条件组合覆盖标准中得到了综合,它要求所有在条件中出现的原子谓词的组合都要覆盖到。
-
路径覆盖:度量一个方法中所有可能路径的覆盖情况。由于循环结构会使得路径的数量剧烈地增长,因此路径覆盖只考虑有限循环次数的情况
解题思路
测试实现技术
断言
断言提供对异常进行检查的能力,但只能在开发阶段使用,并且不能替代常规的异常处理。对断言的使用要确保不会带来任何的副作用,也就是说不会改变实际类的状态。比如,对于以下迭代器iter的断言是不合适的,因为该断言检测后会导致迭代器状态的改变,使其指向了下一个对象。assert iter.next()!=null;
测试框架
Junit和TestNG
JUnit一直是一个单元测试框架,也就是说,其构建目的是促进单个对象的测试;
TestNG则是用来解决更高级别的测试问题,它具有JUnit中所没有的一些特性,比如依赖性测试、参数化测试以及多线程测试等特性。
可测试性构建的原则
设计简单的方法、避免私有方法、优先使用通用方法、组合优于继承、避免隐藏的依赖关系与全局状态。
第十二章 软件项目级管理
配置管理
软件配置管理是一种标识、组织和控制修改的技术,贯穿于整个软件生命周期。
版本管理:
- 一方面要规范化不同开发人员之间的合作方式,必须能够保证一个人的工作不会被其它人意外的覆盖;
- 另一方面是要确保每个人工作的对象是当前需要的版本而且能够为后续开发提供基础。
- 累进式的开发过程可能随时有一些新想法和实现,但随后又被抛弃掉,这就需要有方便回到先前工作状态的机制。
构建管理:
- 构建(Build)管理系统的主要任务是描述最终软件产品的结构和生成过程
发布管理:
- 发布管理(Release)的主要作用是协调在合 适的时间对合适的用户交付合适产品的保证
变更管理:
- 软件过程中某一阶段的变更,均要引起软件配置的变更,这种变更必须严格加以控制和管理,保持修改信息,并把精确、清晰的信息传递到软件过程的下一步骤
项目计划
除了开发的方法、技术以及开发环境,项目管理是项目成功与否的又一重要因素。项目管理主要关注组织和管理层面的内容。项目计划实际上是一个对项目规模、工作量、成本、进度等方面的估算,同时将人员、时间、计算机资源等各类资源统筹安排,对项目的成功起到非常重要的作用。
WBS工作分解:
将任务按照层次的结构由上到下逐步进行分解。
软件规模估算:
对功能点的复杂度进行考虑,简单的可以分为三个级别 :容易、中等和复杂,分别赋予不同的权值。
开发成本估算的模型CoCoMo:
CoCoMo模型最大的用处在于提供工作量估算的公式。
- CoCoMo模型综合了对项目有较大影响的一系列边界条件
- 模型还提供了根据实际情况对影响因子进行调整的机会。
- CoCoMo模型实际的表现与评估者本身的经验也有着直接的关系。
工程网络图
工作任务之间的依赖关系可以通过工程网络图进行展现,其中没有给出最小和最大工作量,而是关注每个工作包持续的时间状况。
确定最小持续时间的路径又被称为是关键路径,每个处于关键路径上的工作包如果遇到计划外的延期则意味着整个项目的拖延。
甘特图(Gantt 图)
项目计划常使用甘特图(Gantt chart)图对计划进行描述,包括工作包、依赖、责任人、完成比例等。
实心菱形标识出里程碑的位置,表示在此位置可以对现有进度进行评审,并可根据需要对计划进行较大调整甚至终止项目。
项目计划跟踪
项目经理的一个重要的任务是随时掌握项目当前的进行状态,识别出潜在的风险或者延期的征兆并快速进行应对。
软件质量
对项目的质量、风险、人员等方面进行监控,只有它们的指标在计划的控制范围之内,项目的进度和成本控制才有意义。
软件质量保证要素标准、评审和审核。
第十三章 软件过程管理与改进
软件企业组织需要建立一个统一的、明确定义的组织级软件过程。
CMMI
CMMI是一个企业级的软件开发过程模型和质量标准,提供了一个组织级的管理模型、标准、改进框架
CMMI级别:
- 初始级:软件过程是无序的,有时甚至是混乱的,对过程几乎没有定义,成功取决于个人努力。管理是反应式的。
- 可管理级:建立了基本的项目管理过程来跟踪费用、进度和功能特性。制定了必要的过程纪律,能重复早先类似应用项目取得的成功经验。
- 已定义级:已将软件管理和工程两方面的过程文档化、标准化,并综合成该组织的标准软件过程。所有项目均使用经批准、剪裁的标准软件过程来开发和维护软件,软件产品的生产在整个软件过程是可见的。
- 量化管理级:分析对软件过程和产品质量的详细度量数据,对软件过程和产品都有定量的理解与控制。管理有一个作出结论的客观依据,管理能够在定量的范围内预测性能。
- 优化管理级:过程的量化反馈和先进的新思想、新技术促使过程持续不断改进。
过程域:
CMMI二级及以上中的子过程又称为是过程域(Process Area)。
每个过程域具有一系列的目标,包括特定目标和通用目标,并给出了它们对应的特定实践和通用实践。
个体软件过程PSP:定义了每位员工阶段性的能力和成熟度。
团队软件过程TSP:对团队软件过程的定义、度量和改革提出了一整套原则、策略和方法