HIT软件构造lab3心得

1实验目标概述
本次实验覆盖课程第 2、3 章的内容,目标是编写具有可复用性和可维护性
的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
语法驱动的编程、正则表达式
API 设计、API 复用
本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学
课表管理),学生不是直接针对每个应用分别编程实现,而是通过 ADT 和泛型等
抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性
和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可
维护性)。
2实验环境配置
Eclipse与git

在这里给出你的GitHub Lab3仓库的URL地址(HIT-Lab3-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab3-1190202013
3实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1待开发的三个应用场景
3.1.1值班表管理
要求:1、每天只有一个员工值班;
2、不能出现某天无人值班的情况;
3、任意两个员工值班时间不能重叠;
4、记录员工值班日期、名字、职位、手机号码;

3.1.2操作系统进程调度管理
要求:1、同一个进程可以在多个不同时间段内多次执行;
2、多个进程执行时的时间段不可重叠;
3、在特定时刻,时间轴上没有进程在执行,即“闲置”;
4、操作系统对进程调度是随机的;
3.1.3大学课表管理
要求:1、课表中有多个课程排课;
2、同一时间段可以有不同的课程(学生选课不同)
3、同一课程每周可以出现一次,也可以安排多次;
4、课表中允许有空白时间段(未安排任何课程);
5、同一课程由同一位老师在同一教室进行;
6、一位老师可以承担课表中的多门课程;
7、课表具有周期性(T=7days);

三个应用的共性与差异:
三个APP均与时间段有关,值班表要求一人只能对应一个时间段,进程与课表管理要求一个对象课可以对应多个时间段。在值班表和进程中,要求各个时间段不能重叠,而课表允许有重叠。在值班表中要求不能有空白的时间段,课表必须具有周期行等等。

3.2面向可复用性和可维护性的设计:IntervalSet
该节是本实验的核心部分。
3.2.1IntervalSet的共性操作
1、IntervalSet empty():创建一个空对象,返回创建的空对象;
2、void insert(long start, long end, L label):插入新时间段,start,end分别表示开始时间和结束时间,label是时间段标签,当存在此标签或者时间段被占用时输出异常;
3、Set labels():获得当前对象中标签集合
4、boolean remove(L label):从当前对象中移除某个标签label所关联的标签,若集合中没有找到指定标签则返回false,否则返回true,即成功移除;
5、long start(L label):返回标签label对应时间段的开始时间
6、long end(L label):返回标签label对应时间段的结束时间
7、boolean checkLegal() ;检查当前程序的合法性
8、void clear():清空当前时间段集合中的所有时间段
9、读取时间段集合链表 LinkedList<Interval> readIntervalSet();

3.2.2局部共性特征的设计方案
采用报告3.4.2中的方案6使用decorator模式进行局部共性特征设计方案。 定义一个装饰器抽象类Decorator继承自IntervalSet接口

下面设立多个子类分别继承自Decorator抽象类。三个局部共性特征为
1、是否允许时间重叠
2、是否允许时间段中有空隙
3、是否允许时间循环呈周期变化
可以看出这三个特征的区别在于插入方法和检查程序合法性两个方法上,因此在子类中分别对这两个函数进行重写即可

3.2.3面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
由于在值班表管理中要求n名员工的值班时间段必须相连,无空白时间段,所以DutyIntervalSet与之前设计的不允许时间冲突和不允许各个时间段中有空白时间段,和之前实现的子类功能高度重合,因此直接继承即可。
noGapIntervalSet中增加了判断某个时间段中是否有循环或者空白时间段的方法boolean checkLegal()。该方法中将时间段集合链表LinkedList<Interval>中的时间段依次遍历,如果出现了当前遍历时间段开始时间大于上一个时间段的结束时间的情况,说明有空白,返回false,否则返回true。
noOverlappingIntervalSet通过对insert函数进行重写,保证原集合内时间段之间不发生重叠。在每次加入新时间段时,判断新加入时间段与集合中原有所有时间段的位置关系。如果出现重合,则输出异常,否则可以加入原集合。

3.3面向可复用性和可维护性的设计:MultiIntervalSet
3.3.1MultiIntervalSet的共性操作
1、IntervalSet empty():创建一个空对象,empty()或不带任何参数的构造函数 ,返回创建的空对象
2、MultiIntervalSet(IntervalSet initial):创建一个非空对象,利用initial中包含的数据创建非空对象,返回创建的非空对象
3、void insert(long start, long end, L label):在当前对象中插入新的时间段和标签;
4、Set labels():获得当前对象中的标签集合:
5、boolean remove(L label):从当前对象中移除某个标签所关联的所有时间段
6、IntervalSetintervals(L label):从当前对象中获取与某个标签所关联的所有时间段:,返回结果表达为IntervalSet的形式,其中的时间段按开始时间从小到大的次序排列。
7、getMap():返回当前的时间段集合
8、boolean checkLegal():检查合法性
9、清空当前的时间段集合
3.3.2局部共性特征的设计方案
针对multiIntervalSet同样面临着是否允许空隙,是否允许时间冲突,是否允许周期循环的三个问题,同样采用方案6的装饰器设计模式,重写multiIntervalSet的insert方法和checkLegal方法,设计出不同功能的子类。
3.3.3面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
面对进程调度的应用设计一个ProcessIntervalSet,这个个性化特征为可以有空隙,但是时间不允许重叠,因此这个类继承自上面实现的无周期,不重叠,可以有间隙的装饰子类,因为需要设计成可视化,所以在这个子类中增加两个show方法,可以查看当前所有的进程调度,也可以查看单独的进程被调度的情况。 面对课程表安排的应用设计一个CourseIntervalSet类,由于插入课程时希望通过星期 几和开始上课的时间进行插入,因此在这个子类中需要增加能够将周数和开始上课的时间对应成时间段的开始时间和结束时间的方法,同时,这个课程表也需要可视化读入,所以增加一个获得具体星期的课表的方法。
3.4面向复用的设计:L
所有的ADT都采用泛型L,L必须是immutable类型的变量。如针对每个应用设计的类Employee、Process、Course等
3.5可复用API设计
将方法写在单独的APIs.java中,采用委派方式
3.5.1计算相似度
针对multiIntervalSet的计算相似度的方法,可以计算两个时间段集合的相似度。两个时间段集合的相似度是通过相似时间/总时间计算的,而相似时间则要求时间段和标签完全一样的时候才叫有效的相似时间。 维护两个IntervalSet,标签使用Integer类型,然后将multi的时间段集合 转换成普通的时间段集合,这样将每个时间段按照开始时间的先后顺序插入其中。这样还可以顺便计算出来两个时间段集合的最长持续时间是多少。然后通过for循环,遍历两个时间段集合的每个时间段,统计有效时间,由此计算出相似度结果。
首先分析相似度的计算:
我们以实验指导的例子为例:
首先我们可以得出对于第一个MultiIntervalSet中标签A,其对应的区域有两个地方[0,5)和[20,25),而在第二个MultiIntervalSet中标签A,其对应的区域有一个为[20,35),而由于不同标签的相似度,只存在于相同的标签对应的时间段,因此我们可以计算出相似度。Similar ratio = 15/35=0.42857。 在介绍方法前,首先简述求相似度的辅助函数:
1.public static long getStartTimerShaft(MultiIntervalSet s) 获得MultiIntervalSet的最早的时间
2.public static long getEndTimeShaft(MultiIntervalSet s) 获得MultiIntervalSet的最晚的时间
3.public static double intervalLength(Set s1) 计算s1中的所有时间段的时间的长度
4.public static Set similarIntervals(IntervalSet s1, IntervalSet s2) 求s1与s2的重合时间段,其中参数为由MultiIntervalSet的label对应的多个Interval构成的Interval, Integer为从小到大的顺序。
3.5.2计算时间冲突比例
时间冲突比例应为计算一个MultiIntervalSet类型中所有labels对应的时间段存在重叠的部分。 首先将所有的label映射到Boolean类型,即可认为当前是否已经遍历过该标签。初始化时候,将label对应的Boolean都设置为false。 在循环MultiIntervalSet中所有标签时,依次将每一个设置为true,表示遍历完成。通过双层循环实现对label的遍历,在遍历一个标签时,若发现另一个标签与其相同或已访问过,则跳过,否则通过调用simlarIntervals函数和intervals函数,检查label标签对应的时间段与待检查标签中是否有相同的部分,若有,则累加,即为冲突时间。 总时间通过对每一个label对应时间段求和即可得。 冲突事件/总时间即为时间冲突比例。
3.5.3计算空闲时间比例
首先通过3.5.1中叙述的getStartTimerShaft(MultiIntervalSET)函数,获得MultiIntervalSet的最早的时间点,再通过getEndTimerShaft(MultiIntervalSET)函数,即可获得MultiIntervalSet的最晚的时间点。 此时即可计算总的时间。 首先将开头时间和结束时间放入Set中,即可得到此时对应的Interval的集合,开始时候,只有一个Interval,代表从头到结尾。 通过对MultiIntervalSet中的labels遍历,依次得到每个label对应的时间段。 当得到每个时间段后,将Set中的包含当前label对应的时间段的时间段拆分成,不包括不包括label的时间段的部分,并将原Interval删除,将新拆分的Intervals放入Set中,最后调用intervalLength函数,即可计算Set中的时间间隔的总长度。 通过空闲时间/总时间即可计算出空闲时间比例。
3.6应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
3.6.1排班管理系统
针对排班管理系统,所需完成的功能为:
Step 1 设定排班开始日期、结束日期,具体到年月日即可。
Step 2 增加一组员工,包括他们各自的姓名、职务、手机号码,并可随时删除某些员工。如果某个员工已经被编排进排班表,那么他不能被删除,必须将其排班信息删掉之后才能删除该员工。员工信息一旦设定则无法修改。
Step 3 可手工选择某个员工、某个时间段(以“日”为单位,最小1天,可以是多天),向排班表增加一条排班记录,该步骤可重复执行多次。在该过程中,用户可随时检查当前排班是否已满(即所有时间段都已被安排了特定员工值班)、若未满,则展示给用户哪些时间段未安排、未安排的时间段占总时间段的比例。
Step 4 除了上一步骤中手工安排,也可采用自动编排的方法,随机生成排班表。 Step 5 可视化展示任意时刻的排班表。可视化要直观明了,可自行设计。 其中step4的自动编排方法秉持着平均的原则,计算出一共要安排多少天,然后用天数除以员工数量,再最后处理剩余的没有安排上的部分,从而完成自动的安排。 如果在自动安排之前已经手动插入了一些安排,那么就清空列表后再进行自动编排
3.6.2操作系统的进程调度管理系统
针对操作系统的进程调度管理系统,所需完成的功能为:
Step 1 增加一组进程,输入每个进程的ID、名称、最短执行时间、最长执行时间;进程一旦设定无法再修改其信息。
Step 2 当前时刻(设定为0)启动模拟调度,随机选择某个尚未执行结束的进程在CPU上执行(执行过程中其他进程不能被执行),并在该进程最大时间之前的任意时刻停止执行,如果本次及其之前的累积执行时间已落到[最短执行时间,最长执行时间]的区间内,则该进程被设定为“执行结束”。重复上述过程,直到所有进程都达到“执行结束”状态。在每次选择时,也可“不执行任何进程”,并在后续随机选定的时间点再次进行进程选择。
Step 3 上一步骤是“随机选择进程”的模拟策略,还可以实现“最短进程优先”的模拟策略:每次选择进程的时候,优先选择距离其最大执行时间差距最小的进程。
Step 4 可视化展示当前时刻之前的进程调度结果,以及当前时刻正在执行的进程。可视化的形式要直观明了,可自行设计。 添加一组进程后,可以启动系统模拟进程调度。进程调度有两种调度策略,一个是随机调度,一个是最短进程优先策略。在这里都采取随机数的方式进行随机调度,同时加入无进程的情况。 在随机进程选择中采用平均随机,即每次从未完成的进程中选择一个,并执行随机数的时间,然后进行下一次随机选择进程,直到所有进程都达到执行时间为止。而最短进程优先策略则增加随机数的产生范围,多余的随机数都指向最短时间的进程,这样最短时间的进程就会被更容易调度。
欢迎界面

输入1

功能为添加进程。

输入2

输入5

3.6.3课表管理系统
针对课表管理系统,所需完成的功能为:
Step 1 设定学期开始日期(年月日)和总周数(例如18);
Step 2 增加一组课程,每门课程的信息包括:课程ID、课程名称、教师名字、地点、周学时数(偶数);
Step 3 手工选择某个课程、上课时间(只能是8-10时、10-12时、13-15时、15-17时、19-21时),为其安排一次课,每次课的时间长度为2小时;可重复安排,直到达到周学时数目时该课程不能再安排;
Step 4 上步骤过程中,随时可查看哪些课程没安排、当前每周的空闲时间比例、重复时间比例;
Step 5 因为课程是周期性的,用户可查看本学期内任意一天的课表结果。 每次课的时间长度为2小时,且只有固定的几个时间段可以上课,因此可以根据星期几和上课的开始时间确定唯一的上课时间,在此通过一个转换即可做到。将weekday*24再加上开始上课的时间,就能确定唯一的上课时间。
简要分析,我们需要一个一个标签对应多个时间段的数据类型,因此选择MultiIntervalSet类型。
CourseScheduleApp运行如下:
课表目录

更新课表信息

添加课程

设置课程上课时间

查看未安排的课程

查看设置完成的课程

3.7基于语法的数据读入
修改“排班管理”应用以扩展该功能。
该模块主要采用正则表达式处理从文件中读入的字符串。 根据提供的文件的格式确定正则表达式,然后通过正则表达式的语法获取每一行的信息。
用每一个正则表达式去匹配每一行,如果匹配上了就获取相应的信息进行处理即可。

在匹配确认格式匹配后,将所读入的内容转化为值班表。 具体运行结果如下: 当读入test1.txt时,具体运行结果如下所示:

3.8应对面临的新变化
3.8.1变化1
代价并不大。只需将值班表中使用的没有空时间段改为允许有空隙,并修改查看排版信息的遍历时所用到的intervalset中的函数(start,end),变为multi-intervalset中的函数(intervals)即可。
同时,由于一个员工可以占据多段时间,随机排班的功能也需要进行修改。新的排版方式是对每一天进行遍历,每一天都随机选择一个员工。
3.8.2变化2
代价很小。只需要再套一层NoOverlapIntervalSet的装饰器就足以应对变化。
3.9Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚change分支和master分支所指向的位置。

4实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 时间段 计划任务 实际完成情况
2021.06.27 10:00-20:00 完成IntervalSet接口和CommonIntervalSet实例 按时完成
2021.06.28 13:00-15:00 重构CommonIntervalSet代码 按时完成
2021.06.28 16:00-19:00 完成MultiIntervalSet接口和Common实例 未完成
2021.06.28 21:00-23:00 完成MultiIntervalSet接口和Common实例 按时完成
2021.06.29 9:00-13:00 完成dutyIntervalSet设计和值班表app 未完成
2021.06.29 15:00-19:00 完成dutyIntervalSet设计和值班表app 按时完成
2021.06.30 7:00-13:00 完成装饰类的书写 按时完成
2021.06.30 15:00-23:00 完成具体类的书写并实现进程调度 按时完成
2021.07.01 7:00-15:00 完成课程表安排和APIs类 按时完成
2021.07.02 7:00-16:00 完成应对变化及代价 按时完成
2021.07.03 7:00-18;00 完成报告 按时完成
5实验过程中遇到的困难与解决途径
遇到的难点 解决途径

产生随机数不会
网络搜索

如何对日期进行处理
网络搜索,了解了Date等方法可以进行日期运算
6实验过程中收获的经验、教训、感想
6.1实验过程中收获的经验和教训
对代码复用的体会更加深刻了
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。
工作量过大、难度过大、deadline过紧。

(8)下周就要进行考试了,你对《软件构造》课程总体评价如何?
对我编程起到了很大的作用,让我了解到如何编出更好的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值