软件构造复习大纲

软件构造

这是我为复习该课程所做的思维导图的Markdown版本,存于此作为记录

在这里插入图片描述

前言

课程定位

高级编程课

位于基础课与专业课之间

课程目标

编程层次升级

功能---->质量
具体---->抽象
编程---->工程
手工---->工具

具体知识

面向关键质量目标
可理解性
可维护性
可复用性
健壮性
时空性能
ADT与OOP
软件代码重构
面向更高级软件系统的高级构造技术
软件设计
设计目标
设计原则
设计模式

Views and Quality Objectives of Software Construction

软件构造的多维度视图和质量目标

软件构造多维度视图

软件构造的三个维度

按阶段划分
构造时
运行时
按动态性划分
时刻
阶段
按层次划分
代码
组件

描述软件系统的模型与视图

构造,时刻,代码
源码,抽象语法树ADT(结构化编程),UML类图
构造,阶段,代码
代码变化的版本控制·
构造,时刻,组件
Package,file,静态连接,library,测试用例,build script
构造,阶段,组件
对单个组件(文件)进行控制,组成项目版本控制
运行,时刻,代码
内存占用
运行,阶段,代码
栈跟踪,多线程管理
运行,时刻,组件
包,lib,动态链接,配置文件,数据库,中间件,网络,硬件
运行,阶段,组件
事件记录,多进程,分配进程

概念及描述方法

分布式程序:需要多个运行程序,分别部署于多个计算机物理环境
代码快照:描述程序运行时内存里变量层面的状态
内存转储(Memory dump):一个包含进程内存拷贝的磁盘文件,包含程序异常退出时的寄存器、调用栈、程序数据等,调试器可以加载转储文件并显示信息
执行跟踪(Execution tracing):软件层面,用日志方式记录程序执行的调用次序
事件日志:系统层面的日志

软件构造重要质量指标

外部质量因素

正确性
软件行为要保证严格符合规约定义行为
测试和调试

####### 发现不正确,消除不正确

防御式编程

####### 编写时就保证正确,try,assert

形式化方法

####### 形式化验证

健壮性
对规约定义以外的行为,软件要做出恰当反应

原句:针对异常情况(不被规约覆盖的情况)的处理,关键在于出现异常(不被规约覆盖的情况)时不要崩溃

非规约任意处理
异常(exception?)处理
可扩展性
规约修改是否容易,为了应对变化的需求

软件规模越大扩展越难

ADT与OOP
模块化,可调整式编程
可复用性
一次开发多次使用
发现共性
兼容性*
多个软件系统相互可以容易地集成

核心是要保持设计的同构性,关键是标准化

标准化文件格式
标准化数据结构
标准化用户接口
……
高效性*
可移植性*
易用性*
功能性*
及时性*

内部质量因素

源码方面:行数Loc,逻辑复杂度
结构方面:低耦合,高内聚为优
代码可读性,易于理解,清晰,大小

核心质量目标

可理解性
代码布局风格注解,语句的构造风格标准,复杂度,ADT规约
包的组织,文件的组织,命名空间
refactoring
版本控制工具
日志跟踪
健壮性
Error处理,异常处理,断言,防御式编程,测试优先的编程
持续集成与回归测试(运行时)
单元测试,集成测试,debug,memory监测
日志跟踪
可复用性
ADT OOP 接口实现分离,继承重载重写,组合代理,多态,子类型与泛型,OO设计模式
API design,框架framework复用,静态链接
动态链接
可维护性与适应性
模块化设计,聚合度耦合度,SOLID,OO设计模式,基于状态的编程,基于语法的编程
SOLID,GRASP
SCM Version Control
表现
代码调优,design pattern
空间复杂度,时间复杂度,memory dump,垃圾收集gc
分布式系统
表现分析
并行,多线程

Testing and Test-First Programming

软件测试与测试优先的编程

软件测试

分类

静态测试与动态测试
范围:单元测试,集成测试,系统测试,验收测试,回归测试
结构测试:黑盒测试,白盒测试

区分

测试:发现错误
调试:识别错误根源,消除错误

测试的困难

离散输入空间内软件行为差异巨大

无统计规律分布规律

穷举与暴力不可能,偶然测试无意义
软件与产品差异大,基于样本的统计数据意义不大

Test Case测试用例

输入+执行条件+期望结果

TDD:Test-First Programming/Test-Driven Development测试优先的编程/测试驱动开发

先写spec再写符合spec的测试用例,再写代码,执行测试,有问题再改直到通过

Unit testing 单元测试

模块隔离容易定位错误

JUnit自动化单元测试

assertXXX

动态检查,需要执行代码

动态检查工具

Junit
VisualVM
AppPerfect
EclEmma

静态检查工具

CheckStyle
SpotBugs
PMD

黑盒测试!

用于检查代码功能不关心实现,检查程序是否符合规约

等价类划分

将输入域划分为等价类
根据每个输入数据所需满足的条件来划分
每个等价类代表对约束 满足/违反的有效数据的集合
假设基础:对相似输入(同一等价类中)的输入,程序将会展示相似的行为。故每个等价类中取一个代表参与测试即可

边界值分析

大量错误发生与边界而非中央
边界加入等价类考虑
典型:0,类型_MAX与MIN,集合首尾

针对多输入的处理

笛卡尔积:全覆盖,代价高
覆盖每个取值:最少一次即可,覆盖度未必高

白盒测试*

根据程序执行路径设计测试用例

基本路径测试,对程序执行路径进行等价类划分,找到代表性简单路径,使代码基本覆盖一次

执行一般先于黑盒

覆盖度

函数覆盖

条件覆盖

语句覆盖

分支覆盖

路径覆盖

集成测试

Travis CI

回归测试

在程序中文档化测试策略

Software Construction Process and Configuration Management

软件构造过程与配置管理

SDLC软件开发生命周期

planning-analysis-design-implementation-testing-integration

传统软件开发模型

瀑布过程waterflow

线性推进
阶段划分清楚
整体推进
无迭代
管理简单
无法适应需求增加或变化

增量模型incremental

线性推进
增量式(多个瀑布的串行)
无迭代
比较容易适应需求的增加

V型

向右下:构造过程
再向右上:测试过程
而横向上,开发中可以同层一起完成

如系统工程师设计系统时可以设计系统测试

原型过程Prototyping

在原型上持续不断的迭代,发现用户的需求
开发,评审,开发,评审

时间代价高,开发质量高

螺旋过程Spiral

多轮迭代遵循瀑布模式
每轮迭代有明确目标,遵循原型过程

风险管控严格,分析后才能进入下一轮迭代

敏捷开发与XP极限编程

Agile Development

通过快速迭代与小规模持续改进来快速适应新变化
agile=增量+迭代
敏捷宣言:四个维度
个体和互动高于流程和工具
工作的软件高于详尽的文档
客户合作高于合同谈判
响应变化高于遵循计划

eXtreme Programming

pair programming

SCM软件配置管理

VCS版本控制系统

SCM=VC+SCI

SCI软件配置项

软件中发生变化的基本单元如文件

基线

软件持续变化中的稳定时刻如对外发布的版本

版本控制分类

版本version
为软件的任一特定时刻的形态指派唯一编号作为身份标识
本地版本控制系统
集中性版本控制系统
分布式版本控制系统

git(SCM)工具

.git

CMDB配置管理数据库

工作目录

暂存区

.git目录中的一个文件

workspace->staging->local repository->remote repository

对象图:有向无环图

箭头指向父亲
分支与合并
HEAD指向一个分支名,分支名指向当前commit
每个commit结点指向一个tree结点

每个tree结点指向多个blob结点
每个blob为一个压缩了的仓库单元文件
commit与之前commit不变的部分,对应于几个版本回指向同一blog
变化对应于指向不同的blog

关键命令
git checkout -b xxxx创建分支,head转移至此
git checkout xxxx转移head至分支xxxx
git branch -d 删除分支
git merge xxxx(位于XXXX时)

若在一条线上,XXXX于xxxx均转移至最新位置处
若不在一条线上,自动合并分支,合并两个版本创建新版本,head指向的master移动至新版本commit处

merge产生的结点至多两个父亲
若merge冲突,修改原文件后commit,分支名自动由xxx|merging恢复为xxx

一般软件构造过程

programming编程

编程语言
建模语言
配置语言

review and static code analysis代码评审

代码评审会议
用工具进行静态代码分析

dynamic code analysis/profiling

动态分析,执行程序,观察现象,收集数据,分析不足
度量状态与性能

debugging and testing

发现程序是否有错误,调试定位错误

refactoring重构

代码重构:在不改变功能的前提下优化代码,且使其容易维护与修改
可以使用工具

狭义软件构造过程*

粗略理解build:build-time到run-time

静态分析,编译(生成文档),链接,测试,打包,安装,部署……

尽量以自动化方式实现

build system

VCT Version-Control Tools
Source Tree
Object Tree
Compilation Tools

CI:持续集成

ant,travis ……

Data Type and Type Checking

数据类型与类型检验

编程中的数据类型

基本数据类型

有值
无ID(不可区分)
immutable
栈中分配内存
代价低
java中有对基本数据类型的包装

对象数据类型

有值
有ID
immutable/mutable
堆中分配内存
代价高
可以形成继承关系

引用类型与对象类型Object o = new Integer(3);

标准称呼:静态类型,实际类型

“引用类型”为Object,
“对象类型”为Integer。

改变变量与改变值

改变变量即改变引用,指向另一个存储空间
改变值,向当前指向的存储空间写入新的值

静态/动态类型检查

java的静态检查

Java是静态类型语言:编译前作类型检查
报错实例:

int a = 1.2;
String b = 1;

静态检查内容
语法错误
类名,函数错误
参数数目错误
参数类型错误
返回值类型错误
python动态类型语言:运行阶段作类型检查
静态类型语言:变量类型编译期确定

静态类型语言动静态类型检查可以都有
动态类型语言:变量类型运行期确定
动态类型语言一般没有静态类型检查

java的动态检查

非法的参数值
除零异常
非法的返回值
返回值类型与预期不匹配
越界
数组等索引非法
空指针
引用null
……

不引发error,但行为不对

数据溢出
除以浮点数零
……

可变/不可变数据类型(mutable,immutable)

可变数据类型的危险性

改变变量!=改变变量的值
即改变指向!=改变所指向的存储空间的内容
没有太多临时拷贝,不会频繁垃圾回收性能更好一些,也适合在多模块间共享数据
安全性要保证较为复杂

不变数据类型

Immutability不变性
final:对于对象数据类型,一旦确定其指向,不允许引用(仅仅是指向)改变(静态类型检查),不是说对象不能被改变。

在接收/返回类型时进行防御式拷贝

用Snapshot图理解数据类型

椭圆

单线椭圆 可变值
多线椭圆 不可变值

箭头

单线箭头 可变引用
多线箭头 不可变引用

复杂数据类型:Arrays and Collections

List,Set,Map

iterator

有用的不可变类型

Collections.unmodifiable(Sorted)List/Map/Set(origin)

只是List的包装,List.add会改变Listcopy

不能说内容不能变化

other wrappers

Design Specification

设计规约

编程语言中的方法与函数

小心变量的作用域

规约Spec:programming for communication

编程文档

类描述,实现接口,涉及子类,构造器总描述,方法总描述,各个方法各自的详细描述
设计时的假设,设计决策

Spec

给“供需双方”都确定了责任并区分责任,调用时双方都要遵守(客户端只需要理解Spec即可)隔离“变化”、降低耦合度,不需要了解具体实现

行为等价性behavioral equivalence

站在客户端角度、根据规约:功能是否等价

Spec:前置条件与后置条件

前置条件:约束For 客户端

后置条件:约束For 开发者

要求前置条件满足时后置条件必须满足

前置条件不满足,方法可做任意事情

不关心局部变量,尽量不使用局部变量

设计规约

规约的强度

更强的规约可以替换更弱的规约
更放松的前置条件+更严格的后置条件 spec变强
使用声明式规约

规约图

点为具体的实现
圈为规约,其中点为符合规约的图

一个好的规约

内聚的
信息丰富的
规约本身足够强
规约本身足够弱
规约中使用抽象类型
衡量check precondition的代价

Abstract Data Type(ADT)

抽象数据类型

抽象与用户自定义数据类型

一个抽象数据类型由他的操作定义

ADT中的操作的分类

数据类型

mutable
提供修改其值的操作
immutable
构造新的对象

操作分类

构造器
生产器
由旧对象return 新对象
观察器
变值器
大部分return void

ADT实例

ADT设计原则

表示独立性Representation Independence (RI)

内部变化不影响外部使用

private

在Java中 实现ADT concepts

测试ADT

观测spec

尝试改变对象

方法测试

针对creator:构造对象之后,用observer去观察是否正确
针对observer:用其他三类方法构造对象,然后调用被测observer,判断观察结果是否正确
针对producer:produce新对象之后,用observer判断结果是否正确

不变性Invariants

表示泄露

防御性拷贝return new,输入拷贝

表示不变性Rep Invariant 与抽象函数Abstraction Function

RA:满射,非单射,未必双射

RI:被映射的R中的元素

Beneficent mutation

不可变类型可以:R空间的表示值变,A空间的表示值不变

Documenting the AF ,RI, and Safety from Rep Exposure

AF:如何解释R中值

RI:R中值如何有效

SFRE:自证清白

private,immutable,defensive coping for mutable,

ADT spec 只能用client可见的东西写

checkRep()

ADT invariants replace preconditions

用ADT不变量取代复杂的preconditions

Object-Oriented Programming(OOP)面向对象编程

OOP基本概念

对象,类,属性,方法

instanceof 尝试将前面的对象类型!!转换为后面类型,成功为true,失败为false

不能判断一个对象是否为父类对象
因为后面为父类是,前面为父类或其子类都可以转换成功,返回true

接口与枚举

接口间可以继承与扩展,一个类可以继承多个接口implements
接口确定(定义)ADT规约,定义变量
接口使用default方法实现某一功能,不同接口冲突,实施类要重写default的方法

若同时继承类与接口,优先继承类的方法
接口可以实现static方法,会自动继承

通过接口,增量式增加功能,不会破坏已经实现的类

OOP独特特征

封装与信息隐藏

使客户端只能使用接口定义方法,不可访问属性

Inheritance and override

继承与重写

接口继承使用小例
接口1:定义共性行为
接口2:扩展新行为
接口3:继续扩展行为
Override重写/覆盖
严格继承extends:不许增加新方法
使用final方法,就不可重写
多多利用super()复用父类代码
重写的时候,不要改变原方法的本意
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常
运行阶段进行动态检查

实际执行时调用哪个方法,运行时决定

父类型中的被重写函数体
不为空:

####### 该方法是可以被直接复用的
####### 对某些子类型来说,有特殊性,可重写父类型中的函数,实现自己的特殊要求

为空:

####### 其所有子类型都需要这个功能
####### 但各有差异,没有共性,在每个子类中均需要重写

抽象类Abstract class
至少保留一个抽象方法的类
位于接口与具体类之间
定位:保留空白特性

然后具体类实现个性行为(只实现父类所有方法就行吗)

抽象方法无大括号,最前有abstract修饰

抽象类abstract在public后?都行吧

多态,子类型,重载

polymorphism,subtyping,overloading

特殊多态:功能重载overload
方便client调用:client可用不同的参数列表,调用同样的函数
根据参数列表进行最佳匹配
重载是编译时多态
public void changeSize(int size, String name, float pattern) {}
重载函数错误情况❌
public void changeSize(int length, String pattern, float size) {}:虽然参数名不同,但类型相同
public boolean changeSize(int size, String name, float pattern) {}:参数列表必须不同
在编译阶段时决定要具体执行哪个方法(与之相反,overridden methods则是在run-time进行dynamic checking)
可以在同一个类内重载,也可在子类中重载
重载与重写
重载:引用类型决定使用的版本,所以可在编译时就决定
重写:对象类型决定被用的版本,因为涉及到对heap中actual instance的考察,只能运行时决定
参数化多态:使用泛型编程Generic
泛型接口泛型方法
擦拭法决定了泛型:

泛型不存在与运行时

不能是基本类型,例如:int;
不能获取带泛型类型的Class,例如:Pair.class;
不能判断带泛型类型的类型,例如:x instanceof Pair;
不能实例化T类型,例如:new T()。
泛型的类之间没有继承关系,不可协变性
由子类获取父类泛型
	Class<IntPair> clazz = IntPair.class;
    Type t = clazz.getGenericSuperclass();
    if (t instanceof ParameterizedType) {
        ParameterizedType pt = (ParameterizedType) t;
        Type[] types = pt.getActualTypeArguments(); 

// 可能有多个泛型类型
Type firstType = types[0];
// 取第一个泛型类型
Class<?> typeClass = (Class<?>) firstType;
System.out.println(typeClass);
// Integer
}

?通配符,只在使用时出现,不在定义时出现
<? extends T> 子类型限定通配符

####### 不可作参数可作返回值

<? super T> 超类型限定通配符

####### 可作参数不可作返回值

<?> 无限定通配符

####### 不可作参数不可作返回值

PECS原则
Producer Extends 说的是当你的情景是生产者类型,需要获取资源以供生产时,建议使用 extends 通配符,因为使用了 extends 通配符的类型更适合获取资源。
Consumer Super 说的是当你的场景是消费者类型,需要存入资源以供消费时,建议使用 super 通配符,因为使用 super 通配符的类型更适合存入资源。
子类型多态
期望不同类型的对象可以统一处理而无需区分,遵循LSP原则
所有子类型实例都可以作为父类型引用
子类型的规约不能弱化超(祖先)类型的规约
instanceof 尝试将前面的对象类型!!转换为后面类型,成功为true,失败为false

不能判断一个对象是否为父类对象
因为后面为父类是,前面为父类或其子类都可以转换成功,返回true

包含多态

动态分派与静态分派

静态分派:编译时决定使用的方法
依赖静态类型定位方法执行版本
重载
动态分派:运行时决定使用的方法
依赖实际类型定位方法执行版本
重写

Java重要方法

Object方法

重写toString
equal&hashCode必须重写,为了实现值语义
value semantics:Only its value is important
if (!(o is instance of Name))
return false;
Name n = (Name)o;
//(Edge<?> edge=(Edge<?>) obj;)
……

设计优良的class

OOP历史

Equality in ADT and OOP

ADT和OOP中的等价性

等价关系equivalence Relation

数学中存在的绝对等价

自反
对称
传递

不可变类型的等价

规约下考虑等价性

==和equals()

引用等价性:==

观察等价性

Object类框架/合约

等价对象必须有相同的hashcode值

equal不重写,调用的就只是==:StringBuilder

String List 等都实现了观察等价性

可变类型的等价

观察等价时,不一定有行为等价性

自动打包与等价性

Construction for Reuse

面向复用的软件构造技术

软件的复用Software Reuse

面向复用编程与基于复用编程

降低开发时间和成本
开发复用组件要充分测试可靠稳定
实现组件标准化,可兼容性
初期成本高,后期成本低

复用性Reusability的衡量

复用机会与复用场合,复用代价

可复用组件的级别与形态学

复用级别

源代码级别!主要
白盒复用
源代码可见,可修改扩展
黑盒复用
源代码不可见,不可修改

只能通过API接口调用

需求,设计,spec,数据,测试用例,文档都可以被复用
模块级别!重点
设计可复用类:继承,重写,重载,参数化多态,子类型多态,组合与委派
复用类接口的方法:继承与委派
库级别
API,包
系统级别
框架

LSP:见下一节

delegation and Composition

dependency

无字段保存对象
使用方法参数来建立delegation

association

有字段保存对象

组合 Composition:更强的Association👆,但难以变化

在rep或构造方法中设定

聚合 Aggregation:更弱的Association👆,可动态变化

在构造方法中传入参数绑定

设计可复用的类(第一层面)

CRP:Composite Reuse Principle

复合复用原则

委托发生在object层面

继承发生在class层面

更倾向于使用委派完成复用
CRP(接口加委派)优于面向共性优于面向具体

设计可复用库libraries与框架frameworks(第二层面与第三层面)

框架复用

白盒框架:继承,子类,重载,重写
通过继承和重写实现功能的扩展,通常的设计模式是模板模式(Template Method);
白盒框架所执行的是框架所写好的代码,只有通过override其方法来实现新的功能,客户端启动的的是第三方开发者派生的子类型。
黑盒框架:委派,组合
通过实现特定接口进行框架扩展,采用的是delegation机制达到这种目的,通常采用的设计模式是策略模式(Strategy)和观察者模式(Observer);
黑盒所预留的是一个接口,在框架中只调用接口中的方法,而接口中方法的实现就依据派生出的子类型的不同而不同,它的客户端启动的就是框架本身。

Construction for Change

面向可维护性的构造技术

软件维护与进化

Software Maintenance and Evolution

可维护性的度量

Metrics of Maintainability

可维护性

可扩展性

灵活性

可适应性

可管理性

支持性

模块化设计与模块化原则

高内聚,低耦合,分离关注点,信息隐藏

检验标准

可分解性
可组合性
可理解性
可持续性
异常保护

设计原则

直接映射
尽可能少的接口
尽可能小的接口
显式接口
信息隐藏

coupling耦合和conhesion聚合

两者常常负相关

面向对象设计原则

OO Design Principles:SOLID

SRP: Single Responsibility Principles

单一责任原则

责任:变化的原因
不应该有多于1个原因让你的ADT发生变化,否则就拆分开

OCP: Open/Closed Principle(

面向变化的)开放/封闭原则

对扩展性的开放
模块的行为应是可扩展的
从而该模块可表现出新的行为以满足需求的变化
对修改的封闭
但模块自身的代码是不应被修改的
扩展模块行为的一般途径是修改模块的内部实现
如果一个模块不能被修改,那么它通常被认为是具有固定的行为

LSP: Liskov Substitution Principle

Liskov替换原则

子类型可以增加方法,但不可删除方法
子类型需要实现抽象类型中的所有未实现方法
子类型中重写的方法不能抛出额外的异常
协变:子类型中重写的方法必须有相同或子类型的返回值或者符合co-variance的参数
更具体
逆变:子类型中重写的方法必须使用同样类型的参数或者符合contra-variance的参数
更抽象 Java目前不支持,解读为overload
子类型化
数组:任意元素可为不同子类
泛型:通配符

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList;

Same or stronger invariants 更强的不变量
Same or weaker preconditions 更弱的前置条件
Same or stronger postconditions 更强的后置条件

ISP: Interface Segregation Principle

接口隔离原则

不能强迫客户端依赖于它们不需要的接口:只提供必需的接口(聚合接口)
继承接口太多太大:无用的实现
客户端不应依赖于它们不需要的方法

DIP: Dependency Inversion Principle

依赖转置原则

抽象的模块不应依赖于具体的模块
具体应依赖于抽象
一个类依赖另一个类时,应尽量依赖其接口而不是具体实现类

如field中声明实例:使用接口,方便更宽泛的使用,以及参数传入,如限制传入Set而不是hashSet

语法驱动的构造

子主题 1

子主题 2

子主题 3

子主题 4

子主题 5

正则表达式regular grammer and regular expression

子主题 1

子主题 7

子主题 8

Design Patterns for Reuse and

Maintainability
面向可复用性和可维护性的设计模式

Creational patterns

创建式模式
Concern the process of object creation

工厂模式

定义工厂接口
实现工厂类
在工厂类中

根据具体需求String
依靠if else 来返回具体产品

CRP原则中的第二棵树:工厂树,接口为根
也可以采用静态工厂方法,不创建工厂类实例
符合OCP原则

Structural patterns

结构式模式
Deal with the composition of classes or objects

适配器模式

将某个类/接口转换为client期望的其他形式
器如其名:帮助你将参数转换为spec要求的格式

如:只有矩阵对角顶点,只有可传入两边长与一点的方法来绘制矩形

新的适配器,可以实现原方法所在类实现的接口,或者创建一个接口,用委派方式调用原先方法,结合自身的计算,从整体上实现适配

装饰器模式

继承组合会引起组合爆炸/代码重复
流程如下
为对象增加不同侧面的特性
对每一个特性构造子类,通过委派机制增加到对象上
客户端需要一个具有多种特性的object,通过逐层的装饰来实现
层层装饰,逻辑分明

Behavioral patterns

行为类模式
Characterize the ways in which classes or objects interact and distribute
responsibility.

策略模式

有多种不同的算法来实现同一个任务
但需要client根据需要动态切换算法,而不是写死在代码里
为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例
形成策略树,接口为根

模板模式

框架:白盒框架
做事情的步骤一样,但具体方法不同
共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现
使用继承和重写实现模板模式
改造的关键:从·原来的代码中的差异性抽出来作为类内部方法,以供实现或重写

迭代器模式

实现方式是在ADT类中实现Iterable接口,该接口内部只有一个返回一个迭代器的方法,然后创建一个迭代器类实现Iterator接口,实现hasnext()、next()、remove()这三个方法

访问器模式

用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
实现了稳定的数据结构和易变的操作耦合问题
容易添加新的visitor而不是对每一个

工程师或经理添加新的观测结果

关键:在外部定义,若有data,怎么观察(观察方法),到底对什么感兴趣,传入数据类内部(而不是在内部实现对怎么观察的揣测用ifelsereturn),在内部调用观察方法,参数的对象使用自身(方便数据的访问,而不是在外面转换类型、ifelse)
得到data树与visitor树,

Construction for robustness and Correctness

面向正确性与健壮性的软件构造

再谈正确性与健壮性

对外接口倾向于健壮,对内实现倾向于正确

度量正确性与健壮性

Java中的Error与Exception

Throwable类

Error一般与虚拟机相关
系统崩溃,虚拟机错误,内存不足,堆栈错误
Exception程序可以处理的异常
RuntimeException运行时异常

checked and unchecked

unchecked范围=Error+RuntimeException

其余为checked

checked必须被显式捕获或传递,否则无法编译

unchecked不用

checked异常会给出异常发生现场的详细信息

unchecked仅打印异常信息

checked处理复杂

unchecked处理简单

unchecked存在,就不一定经历捕获与抛出!!!

Exception Handling

try-catch-finally

printStackTrace()与……in main 的输出不一样

Assertions

默认不开启。启用断言:@Test,VM options+ -ea选项

可见使用场景

内部不变量:判断某个局部变量应该满足的条件,assert x > 0
表示不变量:checkRep()
控制流不变量:例如,若不想让程序走向switch-case的某个分支,则可以用断言直接在分支上assert false;
方法的前置条件:判断传入参数是否满足前置条件
方法的后置条件:判断结果时候满足后置条件

强烈建议

检查前置条件是否满足用异常!增加程序健壮性
检查后置条件是否满足用断言!保证程序正确性

防御式编程Defensive Programming

最好的防御就是不要引入bug

如果无法避免

尝试着将bug限制在最小的范围内

限定在一个方法内部,不扩散

Fail fast:尽快失败,就容易发现、越早修复

The SpotBugs tool

Java 静态代码分析工具

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值