2021年春季学期
计算学部《软件构造》课程
Lab 3实验报告
姓名 |
郭子正 |
学号 |
1190201117 |
班号 |
1936601 |
电子邮件 |
|
手机号码 |
18102152791 |
3.2 面向可复用性和可维护性的设计:IntervalSet<L>
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
3.3 面向可复用性和可维护性的设计:MultiIntervalSet<L>
3.3.1 MultiIntervalSet<L>的共性操作
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
1 实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性 的软件,主要使用以下软件构造技术:
⚫ 子类型、泛型、多态、重写、重载
⚫ 继承、代理、组合
⚫ 语法驱动的编程、正则表达式
⚫ API设计、API复用
本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学课表管理),学生不是直接针对每个应用分别编程实现,而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性和差异性,使ADT有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)。
2 实验环境配置
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。
特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
点击实验指导中的链接,创建仓库,克隆到本地进行开发即可。
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-1190201117)。
https://github.com/ComputerScienceHIT/HIT-Lab3-1190201117
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
DutyRosterApp:设计一个排班表。排班表不允许重叠。理论上也不允许有空白,但是这需要等到全部插入结束之后之后才能判断。
ProcessSchedule:设计一个进程调度器,可以将进程执行时间分割并重新排列,模拟处理器的进程调度。进程之间不可以重叠。
CourseSchedule:设计一个课程表,可以添加课程并选课,课程之间可以重叠。由于每周课程都相同,于是只需要关注一周的课程情况即可。
分析三个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
共性:都需要一个管理时间区间的数据结构,可以向结构内插入新区间。每个区间都有一个标识。
差异:有些情况下(排班表、进程调度)区间不允许重叠,而另外一些情况下(课表管理)区间可以重叠。
3.2 面向可复用性和可维护性的设计:IntervalSet<L>
该节是本实验的核心部分。
3.2.1 IntervalSet<L>的共性操作
- 插入新区间
- 获取结构内所有区间的标志
- 删除区间
- 获取给定标志的区间的开始/结束时间(由于IntervalSet中不允许多个区间共享一个标记,所以这个操作的结果是唯一的)
- 将区间按开始时间排序,返回排好序的列表
- 计算冲突时间的比例
- 计算空闲时间的比例
- 获取/设置该数据结构存放区间的最早开始时间/最晚结束时间
3.2.2 局部共性特征的设计方案
CommonIntervalSet实现局部共性特征,个性特征使用装饰器模式拓展。CommonIntervalSet描述了最基本的区间集合。该ADT使用HashMap将区间的标志和区间信息(开始时间,结束时间,区间标志)联系起来,并且保存区间的最早开始时间/最晚结束时间。
- 插入新区间/删除区间:直接修改HashMap即可。
- 获取区间标志:直接访问HashMap中keySet即可。
- 获取给定标志的区间的开始/结束时间:直接访问对应区间信息即可。
- 将区间按开始时间排序,返回排好序的列表:重写比较器,利用标准库的算法。
- 计算冲突/空闲时间的比例:扫描全部时间点,并查看相邻两个时间点之间是否有区间,再进行计算即可。
- 获取/设置该数据结构存放区间的最早开始时间/最晚结束时间:直接访问ADT内保存的 信息即可。
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
添加一个装饰器NonOverlappableIntervalSet,可以禁止重叠区间。装饰器内只需要重写插入方法。每次插入时,扫描待插入区间对应的时间段,查看是否有其他区间已经占用了此时间段。
面向排班表的类型设计DutyIntervalSet:增加了管理员工的功能以及随机生成排班表的功能。
3.3 面向可复用性和可维护性的设计:MultiIntervalSet<L>
3.3.1 MultiIntervalSet<L>的共性操作
- 插入新区间
- 获取结构内所有区间的标志
- 删除区间
- 获取给定标志的所有区间
- 将区间按开始时间排序,返回排好序的列表
- 计算冲突时间的比例
- 计算空闲时间的比例
- 获取/设置该数据结构存放区间的最早开始时间/最晚结束时间
- 计算两个MultiIntervalSet的相似性
3.3.2 局部共性特征的设计方案
计算相似性:枚举所有时间点,查看在任意两个相邻时间点间两个ADT是否存储了具有相同标记的区间信息,若有则项结果中添加相应的值,否则继续查看下一个时间段。
利用IntervalSet作为其rep,将标签标号作为IntervalSet内区间的标签进行操作。其他方法和IntervalSet中实现方案相同。
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
进程调度ProcessIntervalSet:增加随机调度和最短进程优先的调度方法.
模拟课表CourseIntervalSet : 增加添加课程、选课和获取课表的方法
3.4 面向复用的设计:L
将IntervalSet和MultiIntervalSet中标签的类型泛化,以适应不同场景。
3.5 可复用API设计
API设计在各ADT内部。
3.5.1 计算相似度
public double Similarity(MultiIntervalSet<L> set)
枚举所有时间点,查看在任意两个相邻时间点间两个ADT是否存储了具有相同标记的区间信息,若有则项结果中添加相应的值,否则继续查看下一个时间段。
3.5.2 计算时间冲突比例
public double calcConflictRatio()
扫描全部时间点,并查看相邻两个时间点之间是否有区间,再进行计算。
3.5.3 计算空闲时间比例
public double calcFreeTimeRatio()
扫描全部时间点,并查看相邻两个时间点之间是否有区间,再进行计算。
3.6 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
3.6.1 排班管理系统
使用Calendar类管理日期,将日期转化成长整型进行存储,输出时再将长整数转化回日期进行输出。
存储员工姓名和对应Employee信息的对照表,方便进行查找。由于不允许重名,所以这个方法是可行的。
剩下的功能可以直接调用DutyIntervalSet中的方法直接实现。
3.6.2 操作系统的进程调度管理系统
将进程信息打包成Process类,作为ProcessIntervalSet的标签。所有功能均可以使用ProcessIntervalSet中的方法实现。注意在这个应用中区间的总长度是不断变长的。
3.6.3 课表管理系统
只需要管理一周的课程安排即可。将一周分成35个时间段,对应35个可能的上课时间。对这些时间分别编号并存进CourseIntervalSet即可。其余功能可以借助CourseIntervalSet中的方法完成。
3.7 基于语法的数据读入
修改“排班管理”应用以扩展该功能。
只需利用正则表达式分析文本文件。具体如下:
String pEmployee = "([a-zA-Z]+)\\{([a-zA-Z]+),(\\d{3}-\\d{4}-\\d{4})\\}"; String pPeriod = "Period\\{(\\d{4})-(\\d{2})-(\\d{2}),(\\d{4})-(\\d{2})-(\\d{2})\\}"; String pRoster = "([a-zA-Z]+)\\{(\\d{4})-(\\d{2})-(\\d{2}),(\\d{4})-(\\d{2})-(\\d{2})\\}";
Pattern patternEmployee = Pattern.compile(pEmployee); Pattern patternPeriod = Pattern.compile(pPeriod); Pattern patternRoster = Pattern.compile(pRoster);
Matcher matcherEmployee = patternEmployee.matcher(buf); Matcher matcherPeriod = patternPeriod.matcher(buf); Matcher matcherRoster = patternRoster.matcher(buf); |
之后分别读取各组中的字符串即可。其中需要特殊判断Roster部分和Period部分可能的重合情况。
3.8 应对面临的新变化
3.8.1 变化1
评估之前的设计是否可应对变化、代价如何、如何修改设计以应对变化
可以应对变化,将排班表中的rep改为MultiIntervalSet即可。代价很小(两行代码)。
3.8.2 变化2
评估之前的设计是否可应对变化、代价如何、如何修改设计以应对变化
可以应对变化。将MultiIntervalSet中rep的IntervalSet添加一层装饰器NonOverlappableIntervalSet即可。代价很小(三行代码)。
3.9 Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚change分支和master分支所指向的位置。
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 |
时间段 |
计划任务 |
实际完成情况 |
2021-06-28 |
15:00-17:00 |
设计装饰器结构,写IntervalSet相关测试用例 |
完成,顺便实现了CommonIntervalSet |
2021-06-28 |
19:00-20:00 |
设计IntervalSet相关装饰器 |
完成 |
2021-06-30 |
8:00-12:00 |
设计CommonIntervalSet的接口,并完成测试和实现 |
完成 |
2021-07-01 |
8:00-24:00 |
完成DutyRoster |
发现代码需要重构,未完成 |
2021-07-02 |
13:00-17:00 |
实现并调试DutyRoster |
完成 |
2021-07-02 |
19:00-21:00 |
实现并调试Process |
完成 |
2021-07-03 |
9:00-14:30 |
实现并调试Course |
完成 |
5 实验过程中遇到的困难与解决途径
遇到的难点 |
解决途径 |
计算日期/处理日期相关数据 |
使用Calendar类 |
正则表达式匹配 |
百度相关内容,请教同学 |
|
|
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
(1) 过早优化是万恶之源,会把代码结构弄乱,并且会不断重构。
(2) 为了避免重构,需要在开发初期仔细设计好接口以及规范。尽量降低代码耦合度。
6.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在三个不同的应用场景下使用,你是否体会到复用的好处?
应用场景变化较多,需要考虑的情形较之于面向ADT编程要复杂。复用的好处在于可以减少代码量,提高开发效率和维护性。
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
意义:防止在复杂的应用场景中出现复杂且难以调试的错误。
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
难处在于需要精心设计API才可以使适用范围尽可能扩大。
乐趣在于API可以带来效率提升。
(4) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
语法驱动编程比起传统方式较为高效,可以快速准确地读入数据。
(5) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过五周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
难度:设计规约。设计欠周的规约在后期会带来不便,且经常需要重新设计规约,将已有设计推翻重来。
(6) “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的五个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?
经验:尽可能将共用方法放在上层,个性方法放在下层。
教训:1.使用恰当的设计模式,不可滥用设计模式 2. 设计接口时考虑后期拓展的可能性,尽量避免不得不重构代码的情况。
(7) 关于本实验的工作量、难度、deadline。
如果是在平常情况下,工作量和难度均适中,但是实验所在的时间段临近期末,综合考虑来说时间非常不够用。
(8) 到目前为止你对《软件构造》课程的评价。
这门课讲了一些实用的编程技巧以及指导思想,对以后编程开发有帮助。但是课时被压缩之后内容不完整。