前言
进入最后的复习季了,在这里简单记录一下我的复习过程和总结吧,记录一下错题然后复习一下最近学的设计模式
一、错题思考与分析
1.1 Profiling 的考察
以下关于软件构造过程各阶段的说法,不正确的有____
A Profiling是static code analysis的一种典型形式
B Code review的目的是发现代码中的潜在错误
C Refactoring是在不改变代码功能的前提下重写代码,以消除bug,提高质量
D Build是将软件从开发态转化为可运行状态的过程
正确答案:A
注:
profiling 是指在程序执行过程中,收集能够反映程序执行状态的数据。
这里所收集的数据我们称之为程序的 profile。
1.2 程序的构造次序
以下说法,不正确的是___
A 常规的构造次序是:coding ->refactoring-> testing -> code review-> debugging ->build ->dynamic profiling
B 通过code review和profiling找出可能的bug,通过testing找出真实的bug,通过debug找出bug的根源
C 利用spec构造完备的测试用例,后续对代码的任何修改,都应重新运行测试用例
D Build脚本是由配置语言书写,告知build工具如何一步一步完成自动化build任务
正确答案:A
正常顺序如下:
(1) Programming:有编程语言,也有建模语言,如UML,还有配置语言,如XML、JSON。
(2) Code review、Static code analysis:可以使用工具来发现bug,如CheckStyle, SpotBugs。
(3) Testing:测试,单元测试、集成测试、系统测试…
(4) Debugging:调试
(5) Dynamic code analysis/profiling:在程序运行的过程中查看并发现问题,本课程不涉及这部分
(6) Refactoring:重构不改变功能,只是处于更容易维护的目的对代码优化
(7) Build:第2部分
1.3 git的object graph
针对Git仓库的object graph,以下不正确的说法是__
A 它是一个有向图,边的方向指向产生时间较晚的commit节点
B 一个commit节点可以有0个、1个、2个、多个parent节点
C 一个branch(分支)本质上相对于一个指向特定commit节点的“指针”
D 可以有两个不同的branch指向同一个commit节点
E git commit指令相当于在object graph当前分支HEAD指向的commit基础上,派生出一个新的commit节点。
正确答案:AB
注:A选项的object graph确实是有向图,但是应该是指向产生时间较早的节点
B选项,parent节点只能0、1、2个
object graph知识点的补充:
- 指版本之间的演化关系图,一条边A->B表征了**“在版本B的基础上做变化,形成了版本A”**
- commit是对象图中的节点,多个commit之间的关系一般来说由三种:
1.每个commit指向一个父亲
2.多个commit指向同一个父亲:分支
3,一个commit指向两个父亲:合并
一个branch是一个指向一个commit的名字
HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
1.4 对于方法参数的保持
程序员应该有一个共识,方法的参数应该保持不变,尤其是你的方法的参数是mutable类型的时候
例子:
在这个方法中,方法内部通过set将mutable的参数LIst进行了修改,不符合规范,导致下面出现错误:
1.5 Date和 LocalDateTime
Date是可变类型,LocalDateTime是不可变类型
1.6 防御式拷贝
(1)类中的构造器赋值时使用防御使拷贝
(2)类中的返回值时使用防御使拷贝
1.7 Snapshot Diagram
例子:
1.8 老师强调的几个一定要记住的Snapshot Diagram
List:
set:
map:
1.9 迭代器
Iterator: Iterator是一个可变的类,index是可以修改的,注意list的箭头是双箭头,用final修饰
而且**在迭代器中不要使用remove操作!**因为删除后会继续向前补齐,会出现删除错误!
如果用隐式迭代呢?不可以的,因为这样在遍历subjects的同时也在修改subjects,所以不可以
正确方法:使用iterator的remove方法
1.10 数据类型与类型检验
答案:ABD
A选项可以泛泛地理解为对的,JAVA虚拟机很复杂,局部变量在stack
B选项AVA虚拟机会把int里面常用的等一些东西放在一个缓存中,而对象在堆中,int会快
二、知识点复习
2.1 行为等价性
行为等价性应该站在客户端视角看,如果这两个方法在任何情况下提供的行为都是一样的,那么说明是行为等价性。判断等价应该在指定的条件下
例如:
在val不存在时,在客户端角度,这两个方法是不等价的;
出现很多次时候,也不等价;这两种情况时候的返回值是不一样的
2.2 规约
前置条件:对客户端的约束,使用时候必须满足的条件 ——>requires
后置条件:对开发者的约束,方法结束时候必须满足的条件 ——>effects
关系:
前置条件满足,后置条件必须满足
前置条件不满足,则方法可做任何事情
2.3 三个关键字
前置条件 描述参数:@param (参数是什么,代表什么含义,应该满足什么条件)
后置条件 :如果输入符合如何返回:@return
如果异常情况:@throws
例子(找错误):
- 第一行少一个星号!
- 不应该有requires和effects,没有这两个关键字
- param和return无需将String和boolean写出来,这两个在函数名中已经显现
2.4 比较规约
更强的规约和更弱的规约?
假设规约的强度是S2>=S1
- S2的前置条件更弱
- S2在满足S1的前置条件的条件下, S2的后置条件要更强
即:spec变强,是更放松的前置条件+更严格的后置条件
更强的规约可以替代更弱的规约
对于第二条的“S2在满足S1的前置条件的条件下”,我没可以举例子如下:
在相同的S1的前置条件下,S2的effects与S1是一样的,因为每个val必被找到
PS:图的面积越小,spec越强
2.5 设计规约的几个原则
- spec描述的功能应单一、简单、易理解(如果该规约做了两件事情,要分离成两个方法)
- 规约不能有歧义的
例子:
用户端返回空值的时候,可能是val为空值,也可能是key不存在
- 既要足够强,也要足够弱
- 规约应该用抽象数据类型,比如List而不是ArrayList,因为如果用ArrayList无形给客户更多的要求,用抽象数据型可以给客户端更大的自由度
2.6 设计规约的几个最后练习
答案:ABCE
解析:尤其是B选项,对于有行为等价性的两个方法来说,功能一定是一样的,他们可能展现出不同的性能
2.7 ADT的四类方法!!!(十分重要)
1. Creators 构造器 和类的名称应该一样,两种实现:采用静态方法实现对象的构造,采用new来构造
2. Producers 生产器: 由老的对象返回一个新对象,比如一个String的concat
3. Observers观察器: 观察老的对象返回观察的结果
4. Mutators 变值器: 改变属性的值,返回通常是void,不过也可以返回boolean查看是否失败
返回void一定变值器,但是变值器有可能返回void
PS:creator的返回值:
实际上,类的构造器有返回值,返回的是该类的实例,因此类的构造器返回值类型是当前类,因此无需定义返回值类型。但注意:不能在构造器里显示使用return来返回当前类的对象,因为构造器的返回值是隐式的。只写return;不会报错。”
2.7 ADT的四类方法举例
- Integer.valueof() creator(构造一个Integer的对象)
- BigInteger,mod() producer
- List.addAll() mutator
- String.toUpperCase() producer
- Set.contains() observer
- Map.keySet() observer !!!而不是producer,返回的是一部分
- Collections.unmodifiableList() producer
- BufferedReader.readLine() mutator而不是creator/producer是因为他不会实例化缓冲区或者返回一个新的
缓冲区对象,他是修改第一行来使每次的返回值不一样,实质上是mutator - substring(int start int end) producer 返回值也是String
2.8 ADT的第一个特性:表示独立性(RI)
写代码的顺序:Sepecification -> Representation -> Implementation
表示独立性,client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。但是平常说的RI应该是表示不变量:Rep Invariant
PPT中违反RI的例子:
因为在这里的属性用的public,所以用户端调用可能会通过直接调用属性,但是内部的属性如果改变名字或者实现方式,那么客户端那里的调用就直接失效。改变方法:
(PPT这里的属性应该是private)提供一个返回list的方法让客户端调用即可
如果属性用的public而不是private会出现表示泄露,即:representation exposure:不仅影响不变性,也影响表示独立性,无法在不影响客户端的情况下改变其内部表示
2.9 ADT 测试
如何测试observers,如何测试其他三种
2.10 两个表示空间的关系
三个特点:满射,未必单射,未必双射(有不满足RI的值)
2.11 AF与RI的注意事项
一定注意AF和RI是给程序员看的,而不能写在spec中
客户端可以看到的:
A空间、方法(Creator、Producer、Observer、Mutator)
开发人员:
A空间、R空间、方法、Rep、RI、AF
PPT上的一点小坑分析:
在下面的PPT中,右下角的方法的spec要求将字符集合中的c都删掉,我们挨个分析每个rep:
- 字母严格大于,说明没有一样的字符,删除一个以后仍然满足
- 长度是偶数,删除一个以后明显不符合要求
- 不会出现超过一次,那么直接删除以后也符合RI
- 如果被删除的字符集合是xyyx,那么由于indexof的返回值应该是第一个c的位置,那么得到的是yyx,没有按照spec把x全部都删除
2.12 checkRep 随时检查RI
需要注意的是:
- Observer可以不用进行checkRep
- 在开发阶段可以checkRep,如果对性能影响较大,在交付阶段,可以将checkRep注释掉
到底要在什么方法中checkRep呢?
creator和producer 两个方法都要返回一个对象,所以需要进行checkRep
mutator 因为做了改变所以要check
observer 以防它在途中误操作进行了修改
2.13 有益的mutation (Immutable 类的mutation)
这里说明Immutable 类是可以有的mutation的
2.14 避免表示泄露
-
属性
确保所有的变量都是private
如果用了public那么一定有表示泄露!!!!!改不改不一定,但是一定泄露了。
变量是否加final与表示泄露没有关系
确定可变类型的属性 -
方法上
构造函数传递可变类型的类——拷贝
返回值为可变类型的get函数——拷贝