软件构造知识点汇总

第一章、软件的建设观点与质量目标

多维软件视图

软件构造这门课,我们首先接触的就是多维软件视图:
从时间分为:瞬时(Moment)、周期(Period)
从编码分为:构建(开发)(Build-time)、运行(Run-time)
从代码分为:代码(Code-level)、模块(Component)
软件构造知识点汇总这里每个层次关注的点是不一样的
Build-time:

  1. Code-level:代码逻辑组织
  2. Component-level:代码物理组织
  3. Moment:某一时刻的软件形态
  4. Period:软件形态随时间变化

Run-time:

  1. Code-level:逻辑实体再内存中的呈现
  2. Component-level:物理实体在物理硬件环境中的呈现
  3. Moment:逻辑/物理实体在内存/硬件环境中特定时刻的形态
  4. Period:逻辑/物理实体在内存/硬件环境中的形态随时间变化

软件形态的质量因素

软件质量(外部取决于内部):

  1. 外部:正确性、健壮(鲁棒)性、易扩展性、复用性、兼容性、效率、可移植性、易用性、功能性、时效性
  2. 内部因素:LOC、RUCS(可读、可理解、清晰、大小)

软件构造的5个主要目标:

  1. 代码容易理解
  2. 易用,开发便宜
  3. 低复杂度,易扩展
  4. 健壮性、正确性
  5. 效率

软件测试与测试优先编程

残留缺陷率:每1000行中bug的数目(1~10,0.1 ~ 1,0.01 ~0.1)
测试的主要目的:测试出错误
测试的层面:单元测试、集成测试、系统测试
改完之后再测试:回归测试

测试

{\left\{ \begin{array}{c} 静态测试 :看代码\\ 动态测试:边运行边看\end{array}\right.
{TestDebug\left\{ \begin{array}{c} Test(测试):发现有没有错误 \\ Debug(调试):找到错误在哪\end{array}\right.
{\left\{ \begin{array}{c} 白盒测试:按代码一步一步调试 \\ 黑盒测试:结果正确即可\end{array}\right.
测试用例:输入+执行条件+期望结果(assertEquals)
测试有限编程:先写spec再写测试再写代码
{\left\{ \begin{array}{c} 白盒测试:尽量所有的路径都走一次\\ 黑盒测试:划分等价类(对称自反传递),重点测边界\end{array}\right.
覆盖:路径覆盖>分支覆盖>语句覆盖

软件构建的过程与工具

软件生命周期与配置管理

生命周期:SDLC:重点:软件设计→编码→测试
2种基本模型
{线\left\{ \begin{array}{c} 线性过程\\ 迭代过程\end{array}\right.
模型:瀑布模型、增量模型、原型模型、V字模型、螺旋模型
敏捷开发:敏捷=增量+迭代

软件配置管理(SCM)

SCM:追踪和控制软件的变化
软件配置项(SCI):软件中发生变化的基本单元(多为文件)
基线(baseline):稳定的版本
配置管理数据库(CMOB):变化的信息

版本控制管理(VCS)

{\left\{ \begin{array}{c} 本地版本控制系统:仓库在本地 \\ 集中式版本控制系统: 仓库在一个服务器\\ 分布式版本控制系统:服务器和本地都有仓库\end{array}\right.

Github

软件构造知识点汇总Github:{0/1/2\left\{ \begin{array}{c} 存变化的文件\\ 子节点指向父亲结点,可以有0/1/2个父亲节点\end{array}\right.
软件构造知识点汇总软件构造知识点汇总

数据类型与类型检查

{/null\left\{ \begin{array}{c} 静态类型检查:语法错误(类名,函数名,参数的数目/类型,返回值) \\ 动态类型检查:非法参数,非法返回值,null,数组越界\end{array}\right.
{intlongbooleancharbyteshortfloatStringBigIntegar(IntegerBoolean)\left\{ \begin{array}{c} 基本数据类型:int,long,boolean,char,byte,short,float\\ 对象数据类型:String,BigIntegar,基本数据类型的包装(Integer,Boolean……)\end{array}\right.
软件构造知识点汇总
重载overload:操作名称一样,输入参数不同
{\left\{ \begin{array}{c} 静态类型语言:编译时检查 \\ 动态类型语言:运行时检查\end{array}\right.

immutable与mutable

{StringBuilderDateString\left\{ \begin{array}{c} 可变数据类型:StringBuilder,Date,数组 \\ 不可变数据类型:String,基本数据类型与它们的封装\end{array}\right.
不可变数据类型:

  1. 指向的空间所存的数据不可改变
  2. 关键字final:指向的地址不再改变
  3. 问题:垃圾回收

可变数据结构:
4. 防御使拷贝:return new
软件构造知识点汇总迭代器:Iterator
介绍:List,Set,Map
可变→不可变:Collections.unmodifiableList/Set/Map
注:只能看和读,可以改变封装之前的值改变它的值

spec

spec:
{\left\{ \begin{array}{c} 输入输出,数据类型 \\ 功能和正确性\\ 性能\end{array}\right.
行为等价:基于spec的,再某一soec条件下,效果一样
spec:
{percondition@parampostcondition@return@throws\left\{ \begin{array}{c} 前置条件:percondition(@param) \\ 后置条件:postcondition(@return,@throws)\end{array}\right.
fail fast
spec强度,强于S2>S1同时满足以下条件(其中一个相同):

  1. S2的前置条件更弱
  2. 在S1的前置条件下,S2的后置条件更强
    此时S2可以替代S1(用户喜欢强的,程序员喜欢弱的,强的图更小)

ADT

{(Creators)(Producers)(Observers)(Mutators)mutablevoid\left\{ \begin{array}{c} 构造器(Creators):从无到有\\ 生产器(Producers):根据旧的返回新的\\ 观察器(Observers):返回参数\\ 变值器(Mutators):只有mutable才有,唯一可以是void的,更改数据\end{array}\right.
表示独立性:内部的参数独立
{specRepresentationImplementation\left\{ \begin{array}{c} spec:注释,类名 \\ Representation:具体的存储数据,成员变量 \\ Implementation:方法实现体\end{array}\right.
不变量(Invariants):任何时刻变量都要符合的要求
表示泄露→用private,final
接口(interface):只定义方法与spec

RI与AF

软件构造知识点汇总
AF(Abstraction Function):映射:R(表示空间)→A(抽象空间)

  1. 满射
  2. 不一定单射
  3. R中的非法值没有映射

RI:映射:R→boolean(R中的表示是否合法)
Rep invartiant:条件
注释:RI+AF+Safety from rep exposure

OOP

接口与类

一个接口可以有多个实现类,一个类可以实现多个接口
静态工厂方法:软件构造知识点汇总default方法:在接口中写实现体
接口中不能声明实现体:ArrayList × —— List √
{extendsimplementsextends\left\{ \begin{array}{c} 扩展:接口 extends 接口 \\ 实现:类 implements 接口 \\ 继承:子类 extends 父类\end{array}\right.
父类中没有用final修饰的可以重写:@overide;复用:super()
抽象类(abstract class):至少一种方法是抽象的,没有被实现的

多态

{(overload)publicclass<E><?>List<?extends>\left\{ \begin{array}{c} 重载(overload):同一个函数名,参数一定不同,返回值可以不同 \\ 泛型:public class<E>;也可以用<?>: List<? extends 接口>\\ 子类型多态:继承树,处理顶层即可\end{array}\right.
软件构造知识点汇总Object:equals,hashCode,toString

等价性

等价性:是基于AF的,要求的方法返回值相同
{(==)(ID)(equals)\left\{ \begin{array}{c} 引用等价性(==):对基本类型 (对对象类型比较的是ID)\\ 对象等价性(equals):对对象类型\end{array}\right.
{equals()nullnullhashCode()hashCode()hashCode\left\{ \begin{array}{c} equals():比较等价性,不能对数据修改,与null比较返回null,与hashCode()成对出现\\ hashCode():查询的时候用,相同的hashCode一样即可,不一样的也可以相同\end{array}\right.
equals()=true → hashCode()相同,反之则不行
{observerobserver+muator\left\{ \begin{array}{c} 观察等价性:observer的值相同\\ 行为等价性:observer+muator都一样\end{array}\right.
{observerobserver+muator\left\{ \begin{array}{c} 观察等价性:observer的值相同\\ 行为等价性:observer+muator都一样\end{array}\right.
{immutableequals+hashCodemutable\left\{ \begin{array}{c} immutable:一定要重写equals+hashCode\\ mutable:不是必须的\end{array}\right.

复用度的度量、形态与外部表现

{ProgrammingForReuseProgrammingWithReuse\left\{ \begin{array}{c} Programming For Reuse:面向复用的编程 \\ Programming With Reuse:基于复用的编程\end{array}\right.
复用:
{API()()\left\{ \begin{array}{c} 源代码级别:方法、语句\\ 模块级别:类、接口 \\ 库级别:API(用来调用) \\ 构架级别:框架(自己填写内容)\end{array}\right.
软件构造知识点汇总
{西西()\left\{ \begin{array}{c} 白盒复用:直到复用的东西的实现 \\ 黑盒复用:不知道复用的东西的内部实现(尽量用黑盒)\end{array}\right.
{\left\{ \begin{array}{c} 白盒框架:通过继承扩展 \\ 黑盒框架:通过委派扩展\end{array}\right.
模块复用:
{(inheritance)(delegation)\left\{ \begin{array}{c} 继承(inheritance) \\ 委派(delegation)\end{array}\right.

面向复用的软件构造技术

LSP原则

LSP原则:子类型多态:用父类是可以用子类无条件的替换
为了实现LSP,我们要做到:

  1. 子类型的方法只增不减
  2. 子类型实现了抽象的父类中没有实现的方法
  3. 重写的方法,参数与返回值与父类的相同,不能抛出更多的异常
  4. 子类的spec:RI更强,前置条件pre相同或更弱,后置条件post相同或更强
    软件构造知识点汇总
    {(JAVA)/\left\{ \begin{array}{c} 子类型参数:逆变(JAVA会当作重载) \\ 子类型返回值/异常:协变\end{array}\right.
    {\left\{ \begin{array}{c} 逆变:具体→抽象 \\ 协变:抽象→具体\end{array}\right.
    注:
  5. 泛型不能改变:类型擦除→用通配符解决<? extends 父类/接口>/<? super 子类>
  6. 数组可以:但敲定后就不可变了
    软件构造知识点汇总

委派(Comparator)

用另一个对象
继承与委派:
{(inheritance)(class)(delegation)(object)\left\{ \begin{array}{c} 继承(inheritance):要大部分的功能,类(class)层面的 \\ 委派(delegation):要小部分的功能,对象(object)层面的\end{array}\right.
软件构造知识点汇总
CRP原则:一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
{DependencyAssociationrep()CompositionAssociationAggregationAssociation\left\{ \begin{array}{c} Dependency:临时性的委派,在用的时候以参数方式传入,依赖关系\\ Association:永久性的委派,rep里保存,关联关系(在创建的时候传入)\\ Composition:更强的Association,在创建时自动生成\\ Aggregation:更弱的Association,在创建时传入,可以通过函数更改\end{array}\right.

框架(Framework)的复用

框架的复用:浏览器→写插件
{\left\{ \begin{array}{c} 白盒框架:通过继承来扩展 \\ 黑盒框架:通过委派来扩展\end{array}\right.
软件构造知识点汇总

面向复用的设计模式

  1. Adapter(适配器模式):当新的客户端的输入或输出要求与老的方法不一致时,新建一个适配器,改变参数的样式,再委派给老的方法
  2. Decorator(装饰器模式):将某些功能通过委派的方式委派给同一个接口下的其他实现类 软件构造知识点汇总注:最后一行代码一共有4个Stack
  3. Facade(外观模式):当执行有固定逻辑顺序的几个方法是,通过外观模式一次性按固定的逻辑顺序调用这些方法(也可以一次性调用不同的ADT的方法)
  4. Strategy(策略模式):通过委派传入不同的ADT来以不同的实现方法完成目的
  5. Template method(模板模式):继承与重写→白盒框架
    父类 a = new 子类()
  6. Iterator(迭代器模式):JAVA已写好

软件构造知识点汇总

可维护性的度量与构造原则

维护:纠错性、适应性、完善性、预防性
目标:easy to change(Maintainability可维护性、Extensibility可扩展性、Flexibility灵活性、Adaptability可适应性、Manageability可管理性、Supportability支持性)
OO设计原则:SOLID、GRASP
OO设计模式:
{FactoryMethodPattern,BuilderPatternBridgePattern,ProxyPatternMementoPattern,StatePattern\left\{ \begin{array}{c} FactoryMethod Pattern, Builder Pattern \\ Bridge Pattern, Proxy Pattern\\ Memento Pattern, State Pattern \end{array}\right.
高内聚低耦合
信息隐藏,分离关注点

可维护性指数

软件构造知识点汇总

SOILD原则

{SRP()1OCP()LSP(Liskov)DIP()ISP()()\left\{ \begin{array}{c} SRP原则(单一责任原则): 一个方法只实现一个功能,不是多于一个1个的原因改代码\\ OCP原则(开放-封闭原则): 模块可扩展,在比改变内部的情况下扩展\\ LSP原则(Liskov替换原则):子类型可以无条件的替换父类型\\ DIP原则(依赖转置原则):一个类依赖于抽象类而不是具体的类\\ ISP原则(接口聚合原则): 将大的接口拆分成小的接口(类从实现一个变成实现多个)\end{array}\right.

面型维护的设计模式

{(Proxy)ADTclient(Observer)访(Visitor)\left\{ \begin{array}{c} 工厂方法:客户端尽量用接口,建一个专门用来构造的类→静态工厂方法 \\ 抽象工厂方法: 要一组有固定搭配的对象→多个工厂方法\\ 代理模式(Proxy):隔离ADT与client\\ 观察者模式(Observer):一个对多个,双方互相委派,永久委派\\ 访问者模式(Visitor):双方委派,临时委派\end{array}\right.

Observer与Visitor

1.Observer:
软件构造知识点汇总2.Visitor:
软件构造知识点汇总Visitor与Strategy的区别:
软件构造知识点汇总

设计模式对比:

1.一颗继承树:
软件构造知识点汇总1.1适配器(Adaptor):
软件构造知识点汇总1.2代理模式(Proxy):
软件构造知识点汇总1.3模板模式(Template):
软件构造知识点汇总2.两颗继承树的设计模式:
软件构造知识点汇总2.1策略模式(Strategy):
软件构造知识点汇总2.2遍历器模式(Iterator):
软件构造知识点汇总2.3工厂方法:
软件构造知识点汇总2.4抽象工厂方法:
软件构造知识点汇总2.5观察者模式(Observer):
软件构造知识点汇总2.6访问者模式(Visitor):
软件构造知识点汇总

面向可维护的构造技术

{FSMCtrl+Z\left\{ \begin{array}{c} 基于自动机编程 \\ 基于状态的编程:一个状态一个子类,FSM\\ 备忘录模式:Ctrl+Z\end{array}\right.
语法:正则表达式
语法成分语法继承树):终止(叶)+非终止(非叶)
{===\left\{ \begin{array}{c} 终止:没有“=”再解释 \\ 非终止:还有“=”再解释\\ 递归:“=”左右两端都有\end{array}\right.
html:_ 斜体 _ ; 斜体 :软件构造知识点汇总软件构造知识点汇总# 健壮性与正确性
健壮性:
{/debug\left\{ \begin{array}{c} 处理未期望的行为/错误 \\ 终止执行也要返回错误信息 \\ 信息要有助于debug\end{array}\right.
我们要注意:封闭细节+极端情况
{使使\left\{ \begin{array}{c} 健壮性:容忍错误,使用户容易 \\ 正确性:不满足前置条件的就可以做任何处理,使开发者容易\end{array}\right.
健壮性+正确性=可靠性
我们的接口要做到:
{\left\{ \begin{array}{c} 对外:注重健壮性 \\ 对内:注重正确性\end{array}\right.
术语:
{errordefectbugfaultdefectfaultbugfauilure\left\{ \begin{array}{c} error:程序员犯的错误\\ defect:缺陷,bug的根源 \\ fault:defect ≈ fault,bug \\ fauilure:失效,运行时的外在表现\end{array}\right.
error导致→defect/fault/bug导致→failure
test:对合法和非法的都要测试
Code→Test→Debug

健壮性:错误与异常处理

Throwable:
{Error//Expectiontrycatch\left\{ \begin{array}{c} Error:我们无能为力,由于输入/设备/物理因素导致的 \\ Expection:可处理,用try-catch向上传\end{array}\right.
软件构造知识点汇总

Error

软件构造知识点汇总

Expection:

{RuntimeExpection\left\{ \begin{array}{c} Runtime Expection:不能有的→代码不当导致的,验证后一定可以避免的 \\ 其他:是我们要进行处理的→健壮性编程面向的对象,验证也不一定能避免\end{array}\right.
{RuntimeExpection\left\{ \begin{array}{c} Runtime Expection:不能有的→代码不当导致的,验证后一定可以避免的 \\ 其他:是我们要进行处理的→健壮性编程面向的对象,验证也不一定能避免\end{array}\right.

check与uncheck

{uncheckerror+RuntimeExpectioncheck()\left\{ \begin{array}{c} uncheck:error+RuntimeExpection 不能恢复\\ check:其他的异常,由编译器(静态)检查得出,能恢复\end{array}\right.
软件构造知识点汇总

关键字

{trycatchfinallythrowcheckedunchecked()throws+\left\{ \begin{array}{c} try \\ catch \\ finally\\ throw:扔出checked异常,不建议扔出unchecked(语法上允许)\\ throws:自己的+下面传上来的\end{array}\right.
软件构造知识点汇总
TWR:try(Resource res = ……){……}
throwable:栈结构

正确性:断言与防御式编程

目标:fail fast

断言

assert (期望的结果): “提示信息”;
AssertError→直接结束
判断的内容:RI(表示不变量)、内部不变量、控制流不变量、方法的pre、方法的post
不用assert外部的不受控制的
开发阶段使用,运行时注释掉→影响性能和健壮性
assert的开关:
{eada\left\{ \begin{array}{c} 开:-ea \\ 关:-da\end{array}\right.
{assertexception\left\{ \begin{array}{c} assert:针对正确性,不可以发生的情况 \\ exception:针对健壮性,可以发生的不正常情况\end{array}\right.

防御式编程

对于非法输入:garbage in, garbage out

代码调试(debugging)

Debug的方式

{debugdebug\left\{ \begin{array}{c} 边写边debug \\ 写完再debug\end{array}\right.

诊断

{InstrumentationSystem.out.printloggingDivideandConquerSlicing退FocusondifferenceSymbolicDebuggingDebuggerLearnFromOther\left\{ \begin{array}{c} Instrumentation:System.out.print,logging \\ Divide and Conquer:一块一块运行 \\ Slicing:缩小查找的范围,找和错误相关的代码,倒退\\ Focus on difference:找每次提交的差异\\ Symbolic Debugging:用于学术上,输入符号值→符号表达式\\ Debugger\\ Learn From Other\end{array}\right.

工具

logging

并发

2种并行模式:
{\left\{ \begin{array}{c} 共享内存\\ 消息传递\end{array}\right.
{(Process)线(Thread)\left\{ \begin{array}{c} 进程(Process):私有空间,彼此隔离\\ 线程(Thread):程序内部的控制机制\end{array}\right.

进程

  • 拥有计算机的所有资源
  • 多进程间不共享内存→用消息传递
  • 进程=程序=应用
  • 一个应用可以有多个进程
  • OS支持进程间通信:不仅限于本机,也可以是不同机器间
  • JAVA虚拟机(JVM)通常为单一进程(也可以是多个:ProcessBuilder)

线程

软件构造知识点汇总

  • 进程内的小程序
  • 进程=虚拟机,线程=虚拟CPU
  • 一个线程可分出多个
    软件构造知识点汇总
  • 共享进程资源:内存
  • 难以获得现成的私有内存,但线程有自己的堆栈
  • 线程的协调需要代价
  • 每个应用至少一个线程(主线程,自动创建的)
  • 主线程可以创建其他的线程:接口,Runnable
    软件构造知识点汇总
    学过的-able:
    {Comparable/ComparatorIterable/IteratorObservable/ObesrverThrowableRunnable\left\{ \begin{array}{c} Comparable/Comparator \\ Iterable/Iterator \\ Observable/Obesrver\\ Throwable\\ Runnable\end{array}\right.
    软件构造知识点汇总
    Thread应该start而非run,不能start→Runnable

交错竞争

  • 一个核的每一时刻只能执行一个线程
  • 时间分片→多进程/多线程共享处理:OS自动调度
  • 一般,多核的线程/进程数也多于核数
  • 时间交错
  • 外围的发布访问慢
  • Thread.sleep():自己暂停
  • Thread.interrupt():其他线程请求(不一定答应,sleep时才会答应)
  • Thread.isinterrupt():检测是否暂停
  • Thread.yield():自己放弃占用
  • Thread.join()

线程安全

线程安全:ADT/方法在多线程中执行正确

4种线程安全的方法

{(Confinement)(Immutability)线(ThreadsafeDataType)(synchronization)\left\{ \begin{array}{c} 限制数据共享(Confinement):不共享数据 \\ 共享不可变数据(Immutability):共享只读的数据 \\ 共享线程安全的数据(ThreadsafeDataType)\\ 同步机制(synchronization)\end{array}\right.
说明

  • List,Set,Map都是线程不安全的→解决方法:
    Collection.unmodifiable;
    Collection.synchronizedList/Set/Map(new ArraryList/HashSet/HashMap())
  • 对于线程安全的数据的2种不安全
    {iterator()for(Objectx:xList)(线)\left\{ \begin{array}{c} 迭代器遍历:iterator();for(Object x:xList)\\ 多个操作放在一起(线程安全数据只针对一行代码)\end{array}\right.
  • 编写注释:Thread safety agrument:
  • 同步机制:
    lock:List c = Collection.synchronizedList();
    synchronized(Object lock){操作}→只锁{}内的
    相同的锁对应相同的钥匙
    应该互斥
    可以用自己来加锁:synchronized(this)→Monitor模式
    对于函数:public synchronized void ……(Creater不用)(尽量少用,影响性能)
    {mutable)mutable\left\{ \begin{array}{c} 共享的mutable)→用锁\\ 多个mutable→要用同一个锁\end{array}\right.

死锁

2个地方都有的2把锁,顺序不同,一人拿了一个钥匙
解决方法:

  • 大家lock的顺序一致
  • 用额外的锁同一锁在最外层