2021年春季学期
计算学部《软件构造》课程
Lab 3实验报告
姓名 | 王尧 |
学号 | 1190201812 |
班号 | 1903012 |
目录
1 实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
语法驱动的编程、正则表达式
API 设计、API 复用
本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学课表管理),学生不是直接针对每个应用分别编程实现,而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)。
2 实验环境配置
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。
特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
值班表管理:一个单位由n位员工,在一些时间段中安排值班,每个时间段只有一名员工且在一个时间段必须要有员工,值班表需要记录员工的名字,职位,电话号码,以便于联系
操作系统进程进程调度:进程被调度后在cpu中执行,操作系统决定在不同时间段执行那个进程,并且可以挂起某个正在执行的进程,并且在后续中可以回复被挂起的进程。每个时间段只有一个进程可以执行,其余进程应处于休眠状态,进程的执行可以分为多个时间段,在特定时刻,cpu可进行随机调度
大学课表管理:课程需要特定的教室和特定的教师。假设各周的课表都是完全一样的,同样的课程安排将以“周”为单位进行周期性的重复,直到学期结束;一门课程每周可以出现 1 次,也可以安排多次,且由同一位教师承担并在同样的教室进行;允许课表中有空白时间段;同一个时间段内可以安排不同的课程;一位教师也可以承担课表中的多门课程
相同之处:都包含具有不同特征的时间段集合的对象,每个时间段对应不同的对象
不同之处:有三个维度的差异。①是否允许时间轴上有空白。在应用1中,不允许有空白;在应用2和应用3中,允许有空白。②是否允许不同的 interval 之间有重叠。在应用1和应用2中,不允许有重叠;在应用3中,允许有重叠。③是否包含周期性的时间段。应用 3 中以“一周”为单位重复某个课程,但应用1和应用2中不存在这种情况。
3.2 面向可复用性和可维护性的设计:IntervalSet<L>
该节是本实验的核心部分。
3.2.1 IntervalSet<L>的共性操作
我们采用的是方案二
创建对象
插入时间段
移除时间段
3.2.2 局部共性特征的设计方案
开头的书写
插入操作
判断插入时间段与已有时间段是否产生冲突
删除操作
此外,为判断相同标签下插入相连的时间段,这些时间段是否要进行合并,哦我们设计了一个合并开关方法
这个方法将key置为1,来进行操作
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
对第一个应用场景的处理
首先初始化及各部分含义:
Employee类型的list,用于储存排班表中的员工数据。
IntervalSet<Employee> 类型的时间线表示对象,由IntervalSet中的方法产生。
Bounds是Rosterlist中表示信息的数据结构,建立引用。
Rosterrule用于储存排班表的具体信息。
重写tostring和equals方法
设置排班表要求(截图在下方)
1. 开始时间小于结束时间
2. 添加员工
3. 移除员工
4. 根据姓名查找员工信息
5. 将一个员工加入到值班表中
6. 检查值班表是否排满
7. 生成值班表
8. 显示值班表
9. 显示空闲时间占总时间段的比例
3.3 面向可复用性和可维护性的设计:MultiIntervalSet<L>
3.3.1 MultiIntervalSet<L>的共性操作
共性操作与上面大致相同
除了创建时间段,插入时间段,移除时间段还包含intervalset
3.3.2 局部共性特征的设计方案
如下图所示
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
对剩余两个的处理:
Process的:
数据结构的定义
Processlist存储要调度的进程信息。
Schedule为MultiIntervalSet类型,提供相应的数据结构和方法。
Bounds储存实际的时间段信息。
Timeline 为当前进程执行到的时间。
具体操作:
1. 加入新进程
2. 得到进程运行时间
3. 进程调度函数
4. 检查进程是否都已经结束
5. 显示当前进程和进程调度情况
Course:
数据结构的定义:
Courselist存储课程的相关信息
Weeks为时间段的一个list,第n个元素代表n+1周的课程的时间段信息。
Bounds用于对每一周的课程安排信息的list的引用。
此外,还有
Week为本学期的总周数。
startTime储存学期开始日期
实现类:
基本功能:
1. 初始化学期时间段
2. 设置学期周数和开始时间
3. 添加新课程
4. 检查所添加课程与已有课程是否冲突
5. 检查某一课程学时是否排够
6. 检查所有课程学时数是否已满
7. 安排课程
8. 显示没有排满的课程
9. 显示某周剩余空闲时间比例
10. 显示每一周空闲时间所占比例
11. 计算某一周的课程重复率
12. 计算每一周的重复
13. 通过课程名显示课程信息
14. 显示某一天的时间表
3.4 面向复用的设计:L
设计三个应用的不同标签,分别是员工(Employee),进程(Process),课程(Course)
利用这三个分别完成对CourseIntervalSet,,DutyIntervalSet,ProcessSchedule的书写
3.5 可复用API设计
3.5.1 计算相似度
3.5.2 计算时间冲突比例
3.5.3 计算空闲时间比例
某一周的空闲时间
每一周的空闲时间比例
空闲时间段占总时间段的比例
3.6 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
3.6.1 排班管理系统
排班管理系统我们进行了不同的读入,手动读入和自动读入
选择读入方式(手动/文件)
手动读入
文件读入
3.6.2 操作系统的进程调度管理系统
输入提示:
3.6.3 课表管理系统
3.7 基于语法的数据读入
修改“排班管理”应用以扩展该功能。
定义正则表达式用来匹配文件中数据
再下来,对文件中读入的数据进行分割
并针对不同类型数据进行挑选,对数据进行初始化
3.8 应对面临的新变化
3.8.1 变化1
在最初设计时已考虑此变化,因此代价不大
3.8.2 变化2
在最初设计时已考虑此变化,因此代价不大
3.9 Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚change分支和master分支所指向的位置。
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
6.29-6.30 | 早八点到晚上十点 | Intervalset接口和multiIntervalset接口 | 基本完成 |
7.1 | 早上十点到晚上23时 | 三个子类的实现 | 基本完成三分之一 |
7.2-7.3 | 全天 | 三个字类的实现 | 基本完成 |
5 实验过程中遇到的困难与解决途径
遇到的难点 | 解决途径 |
编写过程中遇到了很多健壮性问题 | 对每一个步骤可能的输入进行分析进行分析,对非法情况做出提示。 |
不知道正则表达式如何完成对文件信息的抽取 | 先查阅资料,然后对正则表达式进行小的书写,然后再进行大的书写 |
装饰器的用法上面不是很懂,你知道如何才能实现我们所需要的功能 | 在网络上查找相应的资料并进行归纳整理,逐步完成装饰器的使用 |
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
设计adt的过程中一定要先考虑,不然写到后面就尾大不掉,只能重新设计,此外,再编写代码时我们遇到的健壮性问题一定要先考虑再写,不然最后的程序会显得比较乱,并且风险也会更高一些。
6.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在三个不同的应用场景下使用,你是否体会到复用的好处?
面向adt的编程需要我们从实际出发进行抽象,并进行设计,有较好的扩展性和复用性。直接面想应用场景编程只针对具体的应用,每次更换场景时,都要重新编程,这样使得扩展性较差,难以进行复杂的应用场景编程。
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
这些工作使得客户端了解各方法的功能但无法得知内部具体实现,可以防止内部变量被客户端恶意修改,时刻检查表示不变量,保证安全性,同时,好的注释对于代码部分来说可以减轻阅读代码的工作。我愿意坚持这么做。
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
因为api面向的场景是广泛的,因此我们开发编译的难度较大,但是,也正因难度较大,我们在编译过程中能体会到不少的乐趣,同时,完成后会有较大的成就感
(4) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
语法驱动编程可以提供很大的便利,方便用户与开发者,并且有很好的可维护性
(5) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过五周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
难度主要来源于设计:对抽象的把握上面。我们把我们涉及到的具体场景进行分析,确立好基本框架,在一步步填充即可
(6) “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的五个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?
接口、抽象类、类的抽象程度一定是逐渐降低的,将所有应用的共性抽象为接口,然后在抽象类以及类中添加新的特性。通过接口的组合可以形成新的接口,并可以具备不同接口中的抽象。类的继承也可以增加更具体的新的特性。同时,使用正确的设计模式可以使得代码的可复用性和可维护性最大化。
(7) 关于本实验的工作量、难度、deadline。
本实验难度较大,但可接受。工作量很大,尤其是时间在考试周,时间有点紧张,希望老师以后能安排的更合理一些
(8) 到目前为止你对《软件构造》课程的评价。
这门课总体来说是蛮好的,就是在一定程度上课时安排的不是很好,尤其是与csapp一起,压力太大了,但是对于我们对编程的把握来说,是有很大好处的。