HIT软件构造 blog5


前言

进入最后的复习季了,在这里简单记录一下我的复习过程和总结吧,记录一下错题然后复习一下最近学的设计模式


一、错题思考与分析

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类型的时候
例子:
方法参数为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

例子(找错误):
错误例子

  1. 第一行少一个星号!
  2. 不应该有requires和effects,没有这两个关键字
  3. param和return无需将String和boolean写出来,这两个在函数名中已经显现

2.4 比较规约

更强的规约和更弱的规约?
假设规约的强度是S2>=S1

  1. S2的前置条件更弱
  2. S2在满足S1的前置条件的条件下, S2的后置条件要更强

即:spec变强,是更放松的前置条件+更严格的后置条件
更强的规约可以替代更弱的规约

对于第二条的“S2在满足S1的前置条件的条件下”,我没可以举例子如下:
在这里插入图片描述
在相同的S1的前置条件下,S2的effects与S1是一样的,因为每个val必被找到

PS:图的面积越小,spec越强
在这里插入图片描述

2.5 设计规约的几个原则

  1. spec描述的功能应单一、简单、易理解(如果该规约做了两件事情,要分离成两个方法)
  2. 规约不能有歧义的
    例子:
    用户端返回空值的时候,可能是val为空值,也可能是key不存在
    在这里插入图片描述
  3. 既要足够强,也要足够弱
  4. 规约应该用抽象数据类型,比如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的四类方法举例

  1. Integer.valueof() creator(构造一个Integer的对象)
  2. BigInteger,mod() producer
  3. List.addAll() mutator
  4. String.toUpperCase() producer
  5. Set.contains() observer
  6. Map.keySet() observer !!!而不是producer,返回的是一部分
  7. Collections.unmodifiableList() producer
  8. BufferedReader.readLine() mutator而不是creator/producer是因为他不会实例化缓冲区或者返回一个新的
    缓冲区对象,他是修改第一行来使每次的返回值不一样,实质上是mutator
  9. substring(int start int end) producer 返回值也是String

2.8 ADT的第一个特性:表示独立性(RI)

写代码的顺序:Sepecification -> Representation -> Implementation
表示独立性,client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。但是平常说的RI应该是表示不变量:Rep Invariant
PPT中违反RI的例子:
违反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:

  1. 字母严格大于,说明没有一样的字符,删除一个以后仍然满足
  2. 长度是偶数,删除一个以后明显不符合要求
  3. 不会出现超过一次,那么直接删除以后也符合RI
  4. 如果被删除的字符集合是xyyx,那么由于indexof的返回值应该是第一个c的位置,那么得到的是yyx,没有按照spec把x全部都删除
    在这里插入图片描述

2.12 checkRep 随时检查RI

需要注意的是:

  1. Observer可以不用进行checkRep
  2. 在开发阶段可以checkRep,如果对性能影响较大,在交付阶段,可以将checkRep注释掉

到底要在什么方法中checkRep呢?
creator和producer 两个方法都要返回一个对象,所以需要进行checkRep
mutator 因为做了改变所以要check
observer 以防它在途中误操作进行了修改

2.13 有益的mutation (Immutable 类的mutation)

这里说明Immutable 类是可以有的mutation的

2.14 避免表示泄露

  1. 属性
    确保所有的变量都是private
    如果用了public那么一定有表示泄露!!!!!改不改不一定,但是一定泄露了。
    变量是否加final与表示泄露没有关系
    确定可变类型的属性

  2. 方法上
    构造函数传递可变类型的类——拷贝
    返回值为可变类型的get函数——拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值