哈工大软件构造课程知识点总结(三)
系列文章目录
哈工大软件构造课程知识点总结(一)
哈工大软件构造课程知识点总结(二)
哈工大软件构造课程知识点总结(三)
哈工大软件构造课程知识点总结(四)
哈工大软件构造课程知识点总结(五)
哈工大软件构造课程知识点总结(六)
文章目录
- 系列文章目录
- 简介
- Chapter 5:Designing Specification
- 规约简要介绍
- 行为等价性
- 规约的设计
- 规约的比较
- 规约画图
- 如何设计好的规约
- Chapter 6:Abstract Data Type
- 抽象数据类型相关概念
- 设计抽象数据类型
- 测试抽象数据类型
- 表示独立性
- 表示不变性
- 抽象函数
- 表示不变性(RI)与抽象函数(AF)之间的关系
- 表示泄露
- 有益的可变性
简介
此文章是2021春哈工大软件构造课程Chapter 5、Chapter 6的知识点总结。
Chapter 5:Designing Specification
规约简要介绍
“方法”是程序的积木,可以被独立开发、测试、复用。使用“方法”的客户端,无需了解内部的具体实现,这就是“抽象” 的思想。
一个完整的方法应包含规约和实现两大部分,如下图示例:
代码本身就蕴含着“设计决策”(如使用final
关键字说明此变量不可变),但这远远不够。我们需要注释形式的“设计决策”(规约)以供自己和他人阅读。
规约(spec)给程序员和用户双方都确定了责任,调用时双方都要遵守。
规约的作用:
- 隔离“变化”,无需通知客户端——“防火墙”
- 解耦,客户端不需了解具体实现
- 提高代码效率
规约的内容:
- 输入/输出的数据类型
- 方法的功能和正确性
- 性能
只讲“能做什么”,不讲“如何实现”!
行为等价性
根据代码的规约,站在客户端视角看行为等价性。
例: 有以下两个方法:
static int findFirst(int[] arr, int val) {for (int i = 0; i < arr.length; i++) {if (arr[i] == val)return i;}return arr.length;
}static int findLast(int[] arr, int val) {for (int i = arr.length - 1; i >= 0; i--) {if (arr[i] == val)return i;}return -1;
}
对于以下规约:
由于两个函数都符合此规约,故此情况下它们等价。
规约的设计
前置条件(precondition):对客户端的约束,客户端使用方法时必须满足的条件,使用关键词requires
表明。
后置条件(postcondition):对开发者的约束,方法结束时必须满足的条件,使用关键词effects
表明。
- 静态类型声明是一种规约,可据此进行静态类型检查(static checking)
- 方法前的注释也是一种规约,但需人工判定其是否满足
契约:如果前置条件满足了,后置条件必须满足;前置条件不满足,则方法可做任何事情(最好还是处理一下,通过failing fast
让客户端发现这一问题)。
规约具体设计规则:
- 参数使用
@param
描述,结果使用@return
、@throws
描述 - 如果可能,将前置条件写入
@param
中,后置条件写入@return
和@throws
中 - 除非在后置条件中声明过,否则方法内部不应该改变输入参数
- 尽量不设计修改输入参数的规约,减少使用可变对象
- 描述的功能要单一、简单、易理解
- 如果规约中需要提到“值”,只能使用抽象空间中的“值”(关联Chapter 6)
一个具体的规约:
补充Chapter 2 黑盒测试部分:
测试用例不能依赖于具体实现,而必须同客户端一样,遵守规约
出处:Chapter 5 课件 P46
规约的比较
可从规约的确定性、陈述性及强度入手进行比较。
假如规约强度S2 >= S1,则有:
- 前置条件S2比S1更弱或相同
- 后置条件S2比S1更强或相同
较强的规约具有更放松的前置条件 + 更严格的后置条件
例:
(1)以下三个规约依次增强:
(2)以下两个规约无法比较强度
相较于第一个规约,第二个的前置条件更弱了;但在满足第一个规约的前置条件的情况下,第二个规约相较于第一个其后置条件也弱化了(没有返回最低索引值)。
当规约被增强时:
- 可满足规约的实现方式更少
- 更多的用户端可以使用
- 实现者(开发者)的自由度更小,责任更重
- 客户端(使用者)责任更轻
规约画图
以find为例:
可以得到以下结论:
- 某个具体实现,若满足规约,则落在其范围内,否则,在其之外
- 程序员可以在规约的范围内自由选择实现方式,客户端无需了解具体使用了哪个实现
- 规约越强,对应的区域越小
如何设计好的规约
- 规约不应太弱,也不能太强(权衡用户使用与实现难度)
- 在规约里使用抽象类型,可以给方法的实现体与客户端更大的自由度
- 是否使用前置条件取决于check的代价和方法的使用范围
Chapter 6:Abstract Data Type
抽象数据类型相关概念
抽象数据类型(ADT)强调“作用于数据上的操作”,程序员和客户端无需关心数据如何具体存储的,只需设计/使用操作即可。
抽象数据类型的特性:
- 可能发生表示泄露
- 抽象函数(abstraction function) [AF]
- 表示独立性(representation independence) [RI]
- 表示不变性(representation invariant)
抽象数据类型的操作分类:
- 构造器(creator):
t* → T
,可能实现为构造函数或静态函数(工厂方法) - 生产器(producer):
T+, t* → T
- 观察器(observer):
T+, t* → t
- 变值器(mutator):
T+, t* → void | t | T
,通常返回void
,也可返回非空(如本身、修改结果等)
注:T代表抽象类型自身,t是其他类型,+表示类型出现一次或多次,*表示类型出现零次或多次。
例:
Integer.valueOf()
– Creatornew ArrayList()
– CreatorArrays.asList()
– CreatorString.concat()
– ProducerBigInteger.mod()
– ProducerString.toUpperCase()
– ProducerList.size()
– ObserverString.length()
– ObserverMap.keySet()
– ObserverList.addAll()
– MutatorBufferedReader.readline()
– Mutator
设计抽象数据类型
规则:
- 设计简洁、一致的操作
- 要足以支持客户端的需要,且用操作满足需要的难度要低
- 选择表示空间与抽象空间,并在代码中写明选择及AF和RI
- 表示泄露的安全声明(safety from rep exposure)
注:AF、RI应在代码中以注释形式写出,而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏。
后两条规则具体示例:
Chapter 6 课件 P82 ~ P84
测试抽象数据类型
- 测试creators, producers, and mutators:调用observers来观察结果是否满足规约
- 测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确
风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效!
表示独立性
client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部规约和客户端。
违反表示独立性的一个示例:
违反原因:ADT修改后客户端代码受影响(无法再使用
get
方法)
保持表示独立性的一个示例:
表示不变性
表示不变性(represetation invariant, RI)可以看作:
- 某个具体的“表示”是否是“合法的”
- 所有表示值的一个子集,包含了所有合法的表示值
- 一个条件,描述了什么是“合法”的表示值
精确记录RI——rep中所有fields何为有效
使用checkrep()
私有方法检查RI:
- 在所有可能改变表示的方法内都要检查
- Observer方法不改变表示,但以防万一建议也要检查
如何建立表示不变性:
- 构造器和生产器在创建对象时要确保不变量为
true
- 变值器和观察器执行时必须保持不变性
- 每个方法返回前,用
checkRep()
检查不变量是否保持
用ADT不变量可取代复杂的前置条件,相当于将复杂的前置条件封装到了ADT内部。
抽象函数
首先引入表示空间与抽象空间的概念:
抽象空间(A空间):客户端看到和使用的值
表示空间(R空间):ADT对于数据的内部表示
ADT开发者要同时关注抽象空间和表示空间,客户端只需关注抽象空间。
表示空间 → 抽象空间的映射关系:
- 抽象空间的每个值一定有表示空间的值与其对应——满射
- 一些抽象空间的值可能有多个表示空间的值与之对应——未必单射
- 表示空间中某些值可能没有对应的抽象空间的值——未必双射
抽象函数(abstraction function, AF):表示空间和抽象空间之间映射关系的函数,即如何去解释表示空间中的每一个值为抽象空间中的每一个值。
精准记录AF——如何解释每一个表示空间的值(映射关系)
表示不变性(RI)与抽象函数(AF)之间的关系
- 不同的内部表示,需要设计不同的AF和RI
- 选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值
- 即使是相同的R、RI,也可能有不同的AF,即“解释不同”
例:
选择字符串作为字符集合的表示方式,一种可能的对应RI、AF如下:
public class CharSet {private String s;// Rep invariant:// s.length() is even// s[0] <= s[1] <= ... <= s[s.length()-1]// Abstraction function:// AF(s) = union of {s[2i], ..., s[2i+1]} for 0 <= i < s.length()/2
则有:
- “ad”、“eeee”、"abcd"满足RI,“adad”、"abc"不满足RI
- AF(“acfg”) = {a, b, c, f, g}
- AF(“tv”) = AF(“ttuv”) = AF(“ttuuvv”)
表示泄露
“表示泄露”不仅影响表示不变性,也影响了表示独立性。
一旦发生,ADT内部表示可能在程序任何位置发生改变(而不是限制在ADT内部)。
除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的不变性,并避免“表示泄露”。
最好的办法就是使用immutable的类型,彻底避免表示泄露!
表示泄露的安全声明——给出理由,证明代码并未对外泄露其内部表示。
有益的可变性
对于不可变的抽象数据类型,它在抽象空间内的抽象值应是不变的,但其内部表示的表示空间取值是可以变化的。
例:一个使用两个int
型变量来表示分数(AF、RI略,表示的分数为numerator / denominator
)的ADT,其toString()
方法可以修改其内部表示(化简分数):
/*** @return 分数的可读字符串表示*/
@override
public String toString() {int g = gcd(numerator, denominator);/* 化简分数,以便输出人更易读的值 */numerator /= g;denominator /= g;if (denominator < 0) {numerator = -numerator;denominator = -denominator;}checkRep();return (denominator > 1) ? (numerator + "/" + denominator): (numerator + "");
}
注意: 这种mutation仅改变了表示空间的值,并未改变抽象空间的值,对于客户端来说是利用了”AF未必单射“,从一个表示空间值变成了另一个表示空间值,但这并不代表不可变的类中可以随意出现mutator方法!
哈工大软件构造课程知识点总结(三)相关推荐
- 哈工大软件构造课程知识点总结(一)
系列文章目录 哈工大软件构造课程知识点总结(一) 哈工大软件构造课程知识点总结(二) 哈工大软件构造课程知识点总结(三) 哈工大软件构造课程知识点总结(四) 哈工大软件构造课程知识点总结(五) 哈工大 ...
- 哈工大软件构造课程知识点总结(二)
系列文章目录 哈工大软件构造课程知识点总结(一) 哈工大软件构造课程知识点总结(二) 哈工大软件构造课程知识点总结(三) 哈工大软件构造课程知识点总结(四) 哈工大软件构造课程知识点总结(五) 哈工大 ...
- 哈工大软件构造期末知识点总结
哈工大软件构造试题构成: 1.30-45分的选择题,10-20题 2.55-70分的简答与设计题,5-6题 考试知识点(不全面): 1.git工具的知识点 2.Java设计模式(一共有23个,主要考察 ...
- 2022哈工大软件构造课程总结与经验分享(复习指导)
一.软构1-3讲 1.软件构造的多维度视图和质量目标 2.软件测试与测试优先的编程 3.软件构造过程与配置管理 二.软构4-8讲 4.数据类型与类型检验 5.设计规约 6.抽象数据类型 (ADT) 7 ...
- 哈尔滨工业大学软件构造课程笔记第三章第四节
3.4 面向对象的编程(OOP) 1. 基本概念:对象.类.属性和方法 对象 现实世界中的对象有两个共同特征:它们都有状态和行为. 识别现实对象的状态和行为是开始考虑面向对象编程的好方法. 对于你看到 ...
- 哈工大18年春软件构造课程讨论题
这是哈工大18年春软件构造课程(徐汉川老师)的讨论题目,少部分答案摘录自课件PPT和网上的资源(链接在文中给出).如有错误还望指出,谢谢. 一.在软件测试过程中,"测试用例的数目" ...
- 2022哈工大软件构造lab1小结(知识点)
哈工大软件构造lab1小结 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 问题一 使用的库 异常处理机制 字符串内容检查 写入文件 问题二 problem 3:画一 ...
- 哈工大软件构造 复习
哈工大软件构造试题构成: 1.30分的选择题 2.70分的简答题 2019考试知识点(重要的,但不涵盖全部考试范围): 1.git工具的知识点(没有考察命令行,考察的主要是git的演变及各部分的作用) ...
- 哈工大软件构造期末复习
系列文章目录 哈工大软件构造期末复习(最终章) 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录 哈工大软件构造期末复习(最终章) 前言 一.github指令 ...
最新文章
- 引擎: 决定数据库存取数据的方式 = 不同的特点 = 不同的用户体验 数据类型: 规定了数据库可以存放哪些数据 约束: 限制存储数据的规则 键...
- mysql设计功能设置表_MySQL数据表的设计
- 随机森林算法 python_Python实现的随机森林算法与简单总结
- 数据库(4)——候选码和主键
- 腾讯云连续四年排名中国音视频解决市场第一,头部厂商中RTC增速第一
- 跨域:Response to preflight request doesn t pass access control check: No Access-Control-Allow-Origin
- python中的进制,python中的十进制到十六进制
- 使用Kmeans聚类分析对复杂的数据进行分类
- 系统学习NLP(十八)--文本分类概述
- python发送多人邮件没有展示收件人问题的解决方法
- Activiti实现流程定义的控制与修改
- 杯具啊,混合语言编程的弊端出现了,兼谈js的开发工具
- Spark worker内存不足导致任务失败,报错Likely due to containers exceeding thresholds, or network issues
- 多台路由器连接,实现全网可达
- LaTex: 多种方法实现图片复杂排列
- tomcat如何调优
- Android平板电脑上的APP应用程序设计须知
- wordpress美化插件
- 1016: 银行利率 Python
- [POI2013] MOR-Tales of seafaring
热门文章
- 【RocketMQ】消息重试、重试次数设置、死信队列
- 云之讯手机号短信验证
- 2021安徽省高考成绩排名查询,2021年安徽高考成绩排名查询系统,安徽高考位次排名表...
- Rust Tokio hyper 协程下载文件工具
- java编程的一个猜数字有趣小游戏
- 平面解析几何----椭圆中焦中三角形的最值问题
- 使用Java计算生辰八字五行属性
- 部署Kubernetes集群(二进制 v1.18.8版)
- 家用双wan口路由器推荐_请推荐双WAN口的有线千兆硬路由器?
- 2021年7月中国编程语言排行榜