软件构造lab3

目录

1 实验目标概述

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

2 实验环境配置

在eclipse的workspace中新建java project,其中每一个目录中的文件夹都是source folder(之前不太了解eclipse时使用过folder,发现没有对应功能,必须使用source folder),从github获取用于测试txt文件,按照实验手册要求的目录结构完成目录构建。
在我的电脑文件夹中进入eclipse的workspace,在本次实验的project文件夹中右击选择git bash here,在git窗口中完成新建、配置仓库(git init新建仓库)。

3 实验过程

3.1 待开发的三个应用场景

1.三个应用简介:
1)第一个应用:值班表管理(DutyRoster):一个单位有n个员工,在某个时间段内(例如寒假1月10日—3月6日期间),每天只能安排唯一1个员工在单位值班,且不能出现某天无人值班的情况;每个员工若被安排值班m天(m>1),那么需要安排在连续的m天内。值班表内需要记录员工的名字、职位、手机号码,以便于外界联系值班员。
2)第二个应用:操作系统进程调度管理(ProcessSchedule):考虑计算机上有一个单核CPU,多个进程被操作系统创建出来,它们被调度在CPU上执行,由操作系统来调度决定在各个时段内执行哪个线程。操作系统可挂起某个正在执行的进程,在后续时刻可以恢复执行被挂起的进程。可知:每个时间只能有一个进程在执行,其他进程处于休眠状态;一个进程的执行被分为多个时间段;在特定时刻,CPU可以“闲置”,意即操作系统没有调度执行任何进程;操作系统对进程的调度无规律,可看作是随机调度。
3)第三个应用:大学课表管理(CourseSchedule):看一下你自己的课表,每一上午8:00-10:00和每周三上午8:00-10:00在正心楼13教室上“软件构造”课程。课程需要特定的教室和特定的教师。在本应用中,我们对实际的课表进行简化:针对某个班级,假设其各周的课表都是完全一样的(意即同样的课程安排将以“周”为单位进行周期性的重复,直到学期结束);一门课程每周可以出现1次,也可以安排多次(例如每周一和周三的“软件构造课”)且由同一位教师承担并在同样的教室进行;允许课表中有空白时间段(未安排任何课程);考虑到不同学生的选课情况不同,同一个时间段内可以安排不同的课程(例如周一上午1-2节的计算方法和软件构造);一位教师也可以承担课表中的多门课程。
2.三个应用场景的异同:
1)相同之处:都涉及到时间段的概念,都使用时间段作为应用的基础ADT。对于时间段的ADT来说,需要有起始时间、结束时间、能表示时间段的标签。
2)不同之处:
a)第一个应用中时间段与时间段之间不能有空隙,第二、三个应用中可以有;
b)第一个应用中每个时间段的标签都是独一无二的(不能出现重复),而第二、三个应用中可以出现起止时间不同、标签相同的时间段;
c)第一、二个应用中时间段需要有起止时间和一个标签即可,而第三个应用需要有起止时间和三个标签(课名、老师、教室)。
d)第三个应用的时间段是周期性的,而前两个不是。
e)第三个应用允许不同标签(课名、老师、教室都不同)的时间段重叠,前两个应用不允许。

3.2 面向可复用性和可维护性的设计:IntervalSet< L >

3.2.1 IntervalSet< L >的共性操作

1.创建一个空对象:empty()
2.在当前对象中插入新的时间段和标签:void insert(long start, long end, L label)
3.获得当前对象中的标签集合:Set labels()
4.从当前对象中移除某个标签所关联的时间段:boolean remove(L label)
5.返回某个标签对应的时间段的开始时间:long start (L label)
6.返回某个标签对应的时间段的结束时间:long end (L label)
7.返回包含所有时间段的信息(标签、起止时间)的集合。Set<CommonInterval< L>> getIntervals();

3.2.2 局部共性特征的设计方案

1.设计一个类CommonIntervalSet< L>来实现接口IntervalSet。
1)fields
private Set<CommonInterval< L>> intervals;
2)Abstraction function:
通过labels函数能获得该时间段集合的所有时间段的标签。
通过remove函数能删除一个标签为label的时间段(若没有这样一个时间段,返回false,否则返回true)。
通过insert函数能将一个起止时间分别为start、end、标签为label的时间段加入该时间段集合。
通过start函数能获得该集合内一个标签为label的时间段的开始时间
通过end函数能获得该集合内一个标签为label的时间段的结束时间
通过getIntervals函数能获得该集合内所有时间段的所有信息(用CommonInterval类存储每个时间段的标签、起止时间)。
通过removeInterval函数删除标签是label,起止时间分别为start、end的时间段。
3)Representation invariant:
任意两个时间段的起止时间不能重叠(其中一个的起始时间必然大于等于另一个的结束时间)。
任意一个时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的{@code L}类型、且不为空。
4)Safety from rep exposure:
CommonInterval类(在下文中解释该类)是immutable的。
采用defensive copy来处理需要访问mutable的intervals的函数。
L是immutable的类型。
intervals是private的。
2.接下来设计一个class来实现CommonIntervalSet中的每一个时间段:CommonInterval。
1)fields
private final long start;
private final long end;
private final L label;
2)Abstraction function:
时间段的起止时间是getStart()、getEnd(),标签是getLabel()。
3)Representation invariant:
时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的{@code L}类型、且不为空。
4)Safety from rep exposure:
使用private和final来确保所有fields不会被修改。

3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)

使用delegation的方法,在该类的基础上定义NonOverlapIntervalSet, NoBlankIntervalSet, PeriodicIntervalSet三个泛型接口,分别用NonOverlapIntervalSetImpl, NoBlankIntervalSetImpl, PeriodicIntervalSetImpl来实现这些接口,他们分别表示不允许重叠的时间段集合、不允许有空时间段的时间段集合、有周期性的时间段集合。他们完成了额外功能的定义,然后开发应用时就可以按照需要调用他们的方法来完成RI的约束。
NonOverlapIntervalSet定义特殊的insert方法来保证插入时间段的时候没有重叠发生。
NoBlankIntervalSet定义checkNoBlank方法来检查当前时间段集合是否有空白时间段存在,并返回一个集合(若没空白时间段,集合是空的)。
PeriodicIntervalSet定义intervals方法来完成周期性的实现,虽然只保存了一周的时间段集合,但查找的时候能查找到所有周的相应时间段。
1)对dutyroster应用:它是不允许重叠、不允许空白、非周期性的,因此调用NonOverlapIntervalSet的insert方法来保证插入时间段的时候没有重叠发生, 调用NoBlankIntervalSet的checkNoBlank方法来保证不能有空白时间段存在。
2)对processschedule应用:它是不允许重叠、允许空白、非周期性的,因此调用NonOverlapIntervalSet的insert方法来保证插入时间段的时候没有重叠发生。
3)对courseschedule应用:它是允许重叠、允许空白、周期性的,因此调用PeriodicIntervalSet的intervals方法来完成周期性的实现,虽然只保存了一周的时间段集合,但查找的时候能查找到所有周的相应时间段。

3.3 面向可复用性和可维护性的设计:MultiIntervalSet

3.3.1 MultiIntervalSet的共性操作

1.通过labels函数能获得该时间段集合的所有时间段的标签。
2.通过remove函数能删除一个标签为label的时间段(若没有这样一个时间段,返回false,否则返回true)。
3.通过insert函数能将一个起止时间分别为start、end、标签为label的时间段加入该时间段集合。
4.通过intervals函数能获得标签为label的所有时间段(按顺序)。
5.通过getAllIntervals函数能获得该时间段集合内所有的时间段。

3.3.2 局部共性特征的设计方案

1.fields:
private Set<IntervalSet> allintervals;
2.Abstraction function:
1)通过labels函数能获得该时间段集合的所有时间段的标签。
2)通过remove函数能删除一个标签为label的时间段(若没有这样一个时间段,返回false,否则返回true)。
3)通过insert函数能将一个起止时间分别为start、end、标签为label的时间段加入该时间段集合。
4)通过intervals函数能获得标签为label的所有时间段(按顺序)。
5)通过getAllIntervals函数能获得该时间段集合内所有的时间段。
6)通过removeInterval函数删除标签是label,起止时间分别为start、end的时间段。
3.Representation invariant:
1)任意两个时间段的起止时间不能重叠(其中一个的起始时间必然大于等于另一个的结束时间)。
2)任意一个时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的{@code L}类型、且不为空。
4.Safety from rep exposure:
1)CommonInterval类是immutable的。
2)采用defensive copy来处理需要访问mutable的IntervalSet类、allintervals的函数。
3)L是immutable的类型。
4)allintervals是private的。

3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)

使用delegation的方法,在该类的基础上定义NonOverlapIntervalSet, NoBlankIntervalSet, PeriodicIntervalSet三个泛型接口,分别用NonOverlapIntervalSetImpl, NoBlankIntervalSetImpl, PeriodicIntervalSetImpl来实现这些接口,他们分别表示不允许重叠的时间段集合、不允许有空时间段的时间段集合、有周期性的时间段集合。他们完成了额外功能的定义,然后开发应用时就可以按照需要调用他们的方法来完成RI的约束。
NonOverlapIntervalSet定义特殊的insert方法来保证插入时间段的时候没有重叠发生。
NoBlankIntervalSet定义checkNoBlank方法来检查当前时间段集合是否有空白时间段存在,并返回一个集合(若没空白时间段,集合是空的)。
PeriodicIntervalSet定义intervals方法来完成周期性的实现,虽然只保存了一周的时间段集合,但查找的时候能查找到所有周的相应时间段。
1)对dutyroster应用:它是不允许重叠、不允许空白、非周期性的,因此调用NonOverlapIntervalSet的insert方法来保证插入时间段的时候没有重叠发生, 调用NoBlankIntervalSet的checkNoBlank方法来保证不能有空白时间段存在。
2)对processschedule应用:它是不允许重叠、允许空白、非周期性的,因此调用NonOverlapIntervalSet的insert方法来保证插入时间段的时候没有重叠发生。
3)对courseschedule应用:它是允许重叠、允许空白、周期性的,因此调用PeriodicIntervalSet的intervals方法来完成周期性的实现,虽然只保存了一周的时间段集合,但查找的时候能查找到所有周的相应时间段。

3.4 面向复用的设计:L

  1. 对dutyroster应用:Employee类
    1)fields
    private final String name;
    private final String job;
    private final String phone;
    2)Abstraction function:
    该员工的姓名是getName(),该员工的职位是getJob(),该员工的电话是getPhone()。
    3)Representation invariant:
    员工的所有信息都是非空字符串,所有fields不可被修改。
    4)Safety from rep exposure:
    使用private和final来确保所有fields不会被修改。
  2. 对processschedule应用:Process类
    1)fields
    private final long id;
    private final String name;
    private final long mintime;
    private final long maxtime;
    2)Abstraction function:
    该进程的id是getID()、姓名是getName()、最短执行时间是getMinTime()、最长执行时间是getMaxTime()。
    3)Representation invariant:
    进程的所有信息都是非空的,所有fields不可被修改。mintime和maxtime均为正数。
    4)Safety from rep exposure:
    使用private和final来确保所有fields不会被修改。
  3. 对courseschedule应用:Course类
    1)fields
    private final String id;
    private final String coursename;
    private final String teachername;
    private final String classroom;
    private final int timeperweek;
    2)Abstraction function:
    该课程的id是getID()、名称是getCourseName()、教师名称是getTeacherName()、教室是getClassroom()、周学时数是getTimePerWeek()。
    3)Representation invariant:
    进程的所有信息都是非空的,所有fields不可被修改;周学时数>0。
    4)Safety from rep exposure:
    使用private和final来确保所有fields不会被修改。

3.5 可复用API设计

3.5.1 计算相似度

采用迭代器进行遍历即可,以其中一个MultiIntervalSe的标签集合为基准寻找另一个MultiIntervalSe中有无与该集合内的时间段标签相同、时间段重合的部分,有的话累加。在过程中不断更新最早的时间和最晚的时间,最后计算重合部分与最晚时间减去最早时间相除的结果。

3.5.2 计算时间冲突比例

对于应用3,采用标记的方式进行时间冲突计算,对于每周七天、每天5节课的课表来说,用7行5列的二维数组进行标记,标记为0代表空闲,标记为1代表有一节课、但不冲突,标记为2代表冲突。

3.5.3 计算空闲时间比例

对于应用3,与3.5.2方法类似,可以采用标记的方式进行时间冲突计算,对于每周七天、每天5节课的课表来说,用7行5列的二维数组进行标记,标记为0代表空闲,标记为1代表有一节课、但不冲突,标记为2代表冲突。
对于应用2,空闲时间就是进程id为0的所有时间段总和,用它除以开始时间和结束时间即可。

3.6 应用设计与开发

3.6.1 排班管理系统

1.设计:
1)整体概述:
a)首先打印欢迎语句:
Welcome to the DutyRosterApp!
然后没退出前,每次循环要求选择功能之前打印提示信息:
Enter a number to choose function!(eg.1)
[0] quit
[1] read a file to build a duty roster
[2] delete an employee
[3] add a roster
[4] check if there is blank
[5] generate a duty roster automatically
[6] show the present duty roster
[7] initialize the employees and period
(顺序:1(或7和多次3)(或7和5), 4和6, 0)
b)通过console完成与用户的交互。
c)
①Abstraction function:
通过checkNoBlank函数能知道当前时间段集合有没有空白时间段。
通过parse函数能通过解析正则表达式文件来代替console完成输入。
通过between函数能获得两个日期之间的间隔时间。
通过main函数完成在console输入以完成要求的功能。
②Representation invariant:
任意两个标签相同的时间段的起止时间不能重叠(其中一个的起始时间必然大于等于另一个的结束时间)。
任意一个时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的Employee类型、且不为空。
完成值班表的构建后,必须没空白时间段(否则不允许退出)。
③Safety from rep exposure:
CommonInterval类是immutable的。
采用defensive copy来处理需获得mutable的fields或类的函数。
Employee是immutable的类型。
所有的fields是private的。
2)main函数的console功能介绍:
a)输入0:退出循环(完成值班表构建)。但如果检查发现有空白段,将不允许退出,打印提示信息。
b)输入1:然后输入txt文件路径,通过解析正则表达式文件来代替console完成输入。
c)输入2:删除一个员工。把以他为标签的时间段都删掉,并将他从保存员工的变长数组中删除。
d)输入3:添加一个时间段(值班记录),用于输入7初始化员工和值班表的起始时间之后。
e)输入4:检查是否有空白时间段,若有把他们打印出来,否则打印”No Blank!”。
f)输入5:随机生成一个值班表,用于输入7初始化员工和值班表的起始时间之后。
g)输入6:打印当前的值班表(按照时间从小到大)。
h)输入7:初始化员工和值班表的起始时间。按照打印的提示信息通过console按格式输入这些信息。
2.测试:
1)
a)测试用例:
1
src/dutyroster/txt/test1.txt
4
6
0
b)测试结果:
软件构造lab3
软件构造lab3

2)
a)测试用例:
1
src/dutyroster/txt/test1.txt
2
ZhangSan
4
6
0
3
ZhangSan{2021-01-10,2021-01-11}
3
LiSi{2021-01-10,2021-01-11}
0
b)测试结果:
这里命令经过了太多次循环,为了便于查看,暂时将功能提示注释掉了。
可以看到,将ZhangSan删除之后,它的时间段也空出来了,这时候就检查出了空时间段,打印了出来,且退出也不能成功。这时候再想将ZhangSan加回去就不行了,需要用其他员工来填补这个空的时间段,就选择与它邻接的LiSi,因为要求每个人的值班时间段是连续的。这时候就发现检查没有空时间段了,可以退出成功。
软件构造lab3

3)
a)测试用例:
7
Employee{
ZhangSan{Manger,139-0451-0000}
LiSi{Secretary,151-0101-0000}
}
20210110,20210306
3
ZhangSan{2021-01-10,2021-01-11}
4
3
ZhangSan{2021-01-12,2021-03-06}
4
b)测试结果:
这里命令经过了太多次循环,为了便于查看,暂时将功能提示注释掉了。
这里我们测试了非文件的读入方式。可以看出读取成功。
除此之外,测试了一个员工不能有多个时间段的约束条件,可以看到在下图红框中,企图两次插入ZhangSan的时间段失败了。空白段依旧在那里。
软件构造lab3

3.6.2 操作系统的进程调度管理系统

1.设计:
1)整体概述:
a)首先打印欢迎语句:
Welcome to the ProcessScheduleApp!
然后没退出前,每次循环要求选择功能之前打印提示信息:
Enter a number to choose function!(eg.1)
[0] quit
[1] add some processes
[2] show the past process running result and the presentprocess information
[3] random running strategy
[4] min-time-first running strategy
(顺序:1, 3或4, 2(多次键入2可以看到进程执行过程,因为每次键入的时候打印当时的进程执行信息,即对比查看能看到进程执行过程), 0)
b)通过console完成与用户的交互。
c)
①Abstraction function:
通过minLong函数能知道当前所有进程中最大执行时间最短的大小是多少。
通过minLongNum函数能知道当前所有进程中最大执行时间最短的进程的id。
通过isEnd函数能知道当前是否所有进程都执行完毕。
通过main函数完成在console输入以完成要求的功能。
②Representation invariant:
任意两个标签相同的时间段的起止时间不能重叠(其中一个的起始时间必然大于等于另一个的结束时间)。
任意一个时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的Process类型、且不为空。
③Safety from rep exposure:
CommonInterval类是immutable的。
采用defensive copy来处理需获得mutable的fields或类的函数。
Process是immutable的类型。
所有的fields是private的。
2)main函数的console功能介绍:
a)输入0:退出循环。
b)输入1:添加用户希望的数量的进程信息,按提示信息的格式在console进行输入。
c)输入2:打印所有过往执行过的进程信息和当前正在执行的进程(如果有的话)。若没有键入过3或4(没执行过),那么打印"It hasn’t been runned!"。
d)输入3:采用随机选择执行进程的策略挑选未结束的进程来执行。
e)输入4:采用最短进程优先策略来选择未结束的进程来执行(即选择最快能执行结束的进程)。
2.测试:
1)测试用例:
1

5

test0
800
1200

test1
300
400

test2
1400
1700

test3
750
900

test4
920
1000
2)测试结果截图:
软件构造lab3

a)随机策略:
由于选择了随机策略功能,需要再键入3。
然后每次键入2都能看到过往执行过的进程信息和当前正在执行的进程(如果有的话)。
可以看到第一次输入2时,最后一行打印的是[running] 0:nop,意味着当前正在挂起(不执行任何进程)当中;而第二次输入2之后,看到没有方括号里是running的输出行了,且比上一次输入2多了几行输出,这就说明两次键入2之间又执行了新的进程,且当前所有进程执行完毕。

b)最短进程优先策略:
由于选择了随机策略功能,需要再键入4。
然后每次键入2都能看到过往执行过的进程信息和当前正在执行的进程(如果有的话)。
可以看到执行一段时间之后,键入2,没有方括号里是running的输出行,当前所有进程执行完毕。对于同样的进程集合(下次键入1之后加入新进程之前),每次选择这个策略的进程执行顺序是固定的。比如我们的测试用例,因为按照最大执行时间从小到大排序是test1、test3、test4、test0、test2,故一定按照这个顺序执行。下面两张图的对比中也佐证了这一点。
软件构造lab3
软件构造lab3

3.6.3 课表管理系统

1.设计:
1)整体概述:
a)首先打印欢迎语句:
Welcome to the CourseScheduleApp!
然后没退出前,每次循环要求选择功能之前打印提示信息:
Enter a number to choose function!(eg.1)
[0] quit
[1] add all courses
[2] set start time and number of weeks
[3] schedule a course
[4] check all courses needed to be scheduled
[5] check the ratio of the blank and the overlap
[6] search courses on the very day
(顺序:1和2, 多次3, 4或5或6)
b)通过console完成与用户的交互。
c)
①Abstraction function:
通过transferToNumber函数能将星期几转换成数字用于求学期内任意一天的课表,他等于这一天转换到第一周之后距离周一的偏移。
通过main函数完成在console输入以完成要求的功能。
②Representation invariant:
任意一个时间段的起止时间是{@code long}类型的非负数,时间段的终止时间大于起始时间,标签是immutable的Employee类型、且不为空。
完成第一周的课表构建后,在weeks周内每周课程都相同。
③Safety from rep exposure:
CommonInterval类是immutable的。
采用defensive copy来处理需获得mutable的fields或类的函数。
Course是immutable的类型。
所有的fields是private的。
2)main函数的console功能介绍:
a)输入0:退出循环(完成课表构建)。
b)输入1:通过console输入加入所有的课程类,但此时还没有安排他们的课表,也就是课表依旧为空。
c)输入2:设置学期的开始时间和学期的周数。在这些周数范围之内,每周课程均与第一周相同,我们只保存第一周的课表,然后调用PeriodicIntervalSet的intervals方法来完成非第一周的课表查询,将查询的时间进行转换。
d)输入3:添加一个课程安排,每次只能安排两个小时,且起止时间必须是8-10或10-12或13-15或15-17或19-21中的一个,调用transferToNumber方法完成星期几到具体日期的转换。
e)输入4:打印所有未安排完的课程(当前课表中该课的每周学时数未达到course类中的要求周学时数)。
f)输入5:检查当前周课表的空闲比例和时间重叠比例,分别打印。
g)输入6:输入学期内任意一天(若超出了范围,不是本学期课表查询,返回为空,且提示错误信息“invalid date”),能获得当天的课表(将该日期的课表查询转换成第一周相应日期的课表查询)。
2.测试:
1)测试用例:
1
3

CSAPP
0
1
8

SC
1
2
4

ADT
2
3
2

2
2020
9
7
18

3
CSAPP
MonDay
0810
4
5
6
20210104

3
SC
MonDay
0810
5
6
20210104
2)测试结果:
这里命令经过了太多次循环,为了便于查看,暂时将功能提示注释掉了。
首先,添加三门课程,分别叫CSAPP、SC和ADT,然后选择功能2、设置学期的开始日期和周期数,然后选择功能3,在课表中插入CSAPP在周一8-10点的课程安排,然后选择功能4、查看课程的未安排情况,发现刚刚插入课表的CSAPP剩余学时数确实少了2学时,然后选择功能5,发现空闲比例发生了变化。然后选择功能6、查询2021年1月4日的课表(这一天是周一且在学期范围内),得到正确的结果。最后,又插入了另一门课程SC,和上一次插入的课程CSAPP放在同一个位置,此时选择功能4查看空闲、冲突比例,发现冲突比例增加了;又选择功能6查看周一的课表,发现课表确实发生了变化。
软件构造lab3

3.7 基于语法的数据读入

调用3.6.1中提到的main函数中键入1可以选择的功能即可。也就是parse函数。
输入1,然后输入txt文件路径,通过解析正则表达式文件来代替console完成输入。这里由于试验给出的测试用例中有全角空格,要使用replace函数将它替换成半角、再通过trim()函数完全消除。

同时,正则表达式的要求要非常规范,比如不能把字母和数字都用.来表示,字母应该用字符集来完成(比如[a-zA-Z]表示所有大、小写的字母),数字的格式是\d{i}(其中i表示数字位数)。

3.8 应对面临的新变化

3.8.1 变化1

1.评估之前的设计是否可应对变化、代价如何:
之前我就是使用的MultiIntervalSet来实现的DutyRosterApp,所以完全可以改变成允许同一个员工有多个时间段的情况。
2.修改设计以应对变化:
将此前的一个员工不能有超过一个时间段的约束条件去掉,所以将员工标记数组去掉,允许任何时间合理(非重叠、无空白)的时间段插入,就可以完成功能了。(将marke数组及相应操作删除)

此时,对于3.6.1中的测试用例3,我们再次测试一下,发现结果完全不同:
1)测试用例:
7
Employee{
ZhangSan{Manger,139-0451-0000}
LiSi{Secretary,151-0101-0000}
}
20210110,20210306
3
ZhangSan{2021-01-10,2021-01-11}
4
3
ZhangSan{2021-01-12,2021-03-06}
4
2)测试结果:
这里命令经过了太多次循环,为了便于查看,暂时将功能提示注释掉了。
左图是变化之前的输出,测试了一个员工不能有多个时间段的约束条件,可以看到在下图红框中,企图两次插入ZhangSan的时间段失败了。空白段依旧在那里。
右图是修改之后的输出,可以实现一个员工多个时间段的约束条件。
软件构造lab3
软件构造lab3

3.8.2 变化2

1.评估之前的设计是否可应对变化、代价如何:
之前我就是调用的MultiIntervalSet来实现的insert(时间段加入操作),这个insert没有约束不能重叠,所以现在只需要调用NonOverlapIntervalSet来实现insert操作就可以完成功能了。
2.修改设计以应对变化:
在CourseIntervalSet类中加入对NonOverlapIntervalSet的调用,用它来实现insert操作。

此时,对于3.6.3中的测试用例,我们再次测试一下,发现结果完全不同:
1)测试用例:
1
3

CSAPP
0
1
8

SC
1
2
4

ADT
2
3
2

2
2020
9
7
18

3
CSAPP
MonDay
0810
4
5
6
20210104

3
SC
MonDay
0810
5
6
20210104
2)测试结果:
这里命令经过了太多次循环,为了便于查看,暂时将功能提示注释掉了。
最后,插入了另一门课程SC,和上一次插入的课程CSAPP放在同一个位置:
变化之前如左图所示,此时选择功能4查看空闲、冲突比例,发现冲突比例增加了;又选择功能6查看周一的课表,发现课表确实发生了变化。
变化之后如右图所示,SC的插入并没有成功,打印了错误提示信息“There is Overlap”,课表中没有SC,冲突比例也没上升。软件构造lab3
软件构造lab3

4 实验过程中遇到的困难与解决途径

遇到的难点 解决途径
eclipse提示我的类不可见,但是是public的,怎么修改都不行。 最后发现是eclipse有bug,有时会会出现延迟很严重的情况,重启就好了。
对于泛型接口的继承不熟练 最后没按照实验指导手册里的框架来写,将非周期性的子类改成了有周期性的子类书写,同时删除了一些报错的继承。
正则表达式的matches总是无法完成功能 我一开始把字母和数字都用.来表示,后来发现字母应该用字符集来完成(比如[a-zA-Z])数字的格式是\d{i}(其中i表示数字位数)。同时,用replace将全角空格转换成半角空格(实验给出的测试用例中含有全角空格),终于匹配成功。
上一篇:ArcGIS Server发布服务,报错001270


下一篇:小贴士3