1 实验目标概述
本次实验覆盖课程第前两次课的内容,目标是编写具有可复用性和可维护性的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
常见的OO设计模式
语法驱动的编程、正则表达式
基于状态的编程
API设计、API复用
本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学课表管理),学生不是直接针对每个应用分别编程实现,而是通过ADT和泛型等抽象技术,开发一套可复用的ADT及其实现,充分考虑这些应用之间的相似性和差异性,使ADT有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)。
2 实验环境配置
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab3-1190300107.git
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
简要介绍三个应用。
分析三个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
(1) 值班表管理:一个单位有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节的计算方法和软件构造);一位教师也可以承担课表中的多门课程;
共同点,都需要一个时间轴,开始时间,结束时间,一些事件标签。插入事件很相似,调度基本一致,有插入,删除,查看标签等操作,基本事件都是不可变类型
区别:三个场景需要不同的时间标签ADT,值班表的标签是Employee,操作系统的标签是ProcessSchedule,大学课表管理的标签是Course。值班表要求同一个人,排班必须集中在某一段连续的时间,操作系统和课程表不要求集中在某一段连续的时间之中。
3.2 面向可复用性和可维护性的设计:IntervalSet
该节是本实验的核心部分。
3.2.1 IntervalSet的共性操作
/**
* 向集合中添加一个时间段
void insert(long start, long end, L label);
/**
* 返回当前对象的标签集合
Set labels();
/**
* 移除一个标签相关联的时间段
boolean remove(L label);
/**
* 返回时间段集合的总的开始时间
long startall();
/**
* 返回时间段集合的总的结束时间
long endall();
/**
* 返回时间轴
/
List<Intervel> getList();
/*
* 创建一个空对象
* @param L 必须是immutable
* @return 一个新的空时间轴
*/
static IntervelSet<?> empty(long start, long end)
{
return new CommonIntervelSet<>(start,end);
}
/**
* 返回某个标签对应的时间段的开始时间
* @param 一个标签
* @return 这个标签对应时间段的开始时间,如果不存在这个标签就
* 返回-1
*/
public long start (L label);
/**
* 返回某个标签对应的时间段的结束时间
* @param 一个标签
* @return 这个标签对应时间段的结束时间,如果不存在这个标签就
* 返回-1
*/
public long end (L label);
3.2.2 局部共性特征的设计方案
采用装饰设计
将 CommonIntervalSet 看作是原始的、未被装饰的对象,将这三个维度看 作是三种“装饰”(每个维度的不同特征取值可以产生不同的“装饰”效果)。然后在具体应用中通过为一个 CommonIntervalSet 对象逐层装饰三个不同特征,即可实现应用所需的组合特征
定义一个装饰类
public abstract class SuperDecorator implements SuperIntervelSet{
然后再派生出子类,增加新的功能
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
DutyIntervalSet由CommonIntervelSet继承而来,同时使用装饰设计,再原来的功能上增加检测空白时间段和重叠时间段的功能
3.3 面向可复用性和可维护性的。设计:MultiIntervalSet
3.3.1 MultiIntervalSet的共性操作
/**
* 向集合中添加一个时间段
void insert(long start, long end, L label);
/**
* 返回当前对象的标签集合
Set labels();
/**
* 移除一个标签相关联的时间段
boolean remove(L label);
/**
* 返回时间段集合的总的开始时间
long startall();
/**
* 返回时间段集合的总的结束时间
long endall();
/**
* 返回时间轴
/
List<Intervel> getList();
/*
* 创建一个空对象
* @param L 必须是immutable
* @return 一个新的空时间轴
*/
static IntervelSet<?> empty(long start, long end)
{
return new CommonIntervelSet<>(start,end);
}
/**
* 返回某个标签对应的时间段的开始时间
* @param 一个标签
* @return 这个标签对应时间段的开始时间,如果不存在这个标签就
* 返回-1
*/
public long start (L label);
/**
* 返回某个标签对应的时间段的结束时间
* @param 一个标签
* @return 这个标签对应时间段的结束时间,如果不存在这个标签就
* 返回-1
*/
public long end (L label);
//Constructor
static MultiIntervelSet<?> empty(long start, long end) {
return new CommonMultiIntervelSet<>(start, end);
}
/**
* 从当前对象中获取与某个标签所关联的所有时间段
* @param label
* @return
*/
public IntervelSet<Integer> intervals(L label)
3.3.2 局部共性特征的设计方案
采用装饰设计
将 CommonMultiIntervalSet 看作是原始的、未被装饰的对象,将这三个维度看 作是三种“装饰”(每个维度的不同特征取值可以产生不同的“装饰”效果)。然后在具体应用中通过为一个 CommonMultiIntervalSet 对象逐层装饰三个不同特征,即可实现应用所需的组合特征
定义一个装饰类
public abstract class SuperDecorator implements SuperIntervelSet{
然后再派生出子类,增加新的功能
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)
CourseIntervelSet由CommonMultiIntervelSet继承而来,并增加了构造方法,初始化时间段的两端
同理,ProcessIntervelSet也是由CommonMultiIntervelSet继承而来,并使用装饰设计的模式,增加了新的功能,检测是否存在重叠的时间段