1 实验目标概述
目标是编写具有可复用性和可维护性的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
语法驱动的编程、正则表达式
API 设计、API 复用
本次实验给定了三个具体应用(值班表管理、操作系统进程调度管理、大学 课表管理),通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充分考虑这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)
2 实验环境配置
本次实验环境的配置与以往的相同,不需要额外配置其他的环境,根据Lab0中的操作指示,在本地建立git 仓库,依次输入以下指令:
git init
git add README.md
git commit -m “init”
git remote add origin https://github.com/ComputerScienceHIT/HIT-Lab3-.git
git push -u origin master
git status
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号):
https://github.com/ComputerScienceHIT/*****
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)
3.1 待开发的三个应用场景
简要介绍三个应用。
分析三个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
值班表管理(DutyRoster):一个单位有 n 个员工,在某个时间段内(例如寒假 1 月 10 日到 3 月 6 日期间),每天只能安排唯一一个员工在单位值班,且不能出现某天无人值班的情况;每个员工若被安排值班 m 天(m>1),那么需要安排在连续的 m 天内。值班表内需要记录员工的名字、职位、手机号码,以便于外界联系值班员。
操作系统进程调度管理(ProcessSchedule):考虑计算机上有一个单核 CPU,多个进程被操作系统创建出来,它们被调度在 CPU 上执行,由操 作系统决定在各个时段内执行哪个线程。操作系统可挂起某个正在执行的进程,在后续时刻可以恢复执行被挂起的进程。可知:每个时间只能有一个进程在执行,其他进程处于休眠状态;一个进程的执行被分为多 个时间段;在特定时刻,CPU 可以“闲置”,意即操作系统没有调度执行任何进程;操作系统对进程的调度无规律,可看作是随机调度。
大学课表管理(CourseSchedule):课程需要特定的教室和特定的教师。在本应用中,我们对实际的课表进行简化:针对某个班级,假设其各周的课表都是完全一样的(意即 同样的课程安排将以“周”为单位进行周期性的重复,直到学期结束); 一门课程每周可以出现 1 次,也可以安排多次且由同一位教师承担并在同样的教室进行;允许课表中有空白时间段(未安排任何课程);考虑到不同学生的选课情况不同,同一个时间段内可以安排不同的课程(例如周一上午 1-2 节的计算方法和软件构造);一位教师也可以承担课表中的多门课程。
三个场景的异同点:
创建一个新计划项;为对应的计划项申请资源;启动、取消、完成指定的计划项;获得计划项的状态;获得每个计划项的名字。
3.2 面向可复用性和可维护性的设计:IntervalSet
3.2.1 IntervalSet的共性操作
为接口 IntervalSet设计应提供的共性接口方法,下面表格是IntervalSet的设计实现:
创建一个空对象 empty()
在当前对象中插入新的时间段和标签 void insert(long start,
long end, L label)
获得当前对象中的标签集合 Set labels()
从当前对象中移除某个标签所关联的时间段 boolean remove(L
label)
返回某个标签对应的时间段的开始时间 long start (L label)
返回某个标签对应的时间段的结束时间 long end (L label)
3.2.2 局部共性特征的设计方案
本次实验中,我选择方案6来完成局部共性特征的设计,将 CommonIntervalSet 看作是原始的、未被装饰的对象,将这三个维度看 作是三种“装饰”(每个维度的不同特征取值可以产生不同的“装饰”效果)。参照讲义上关于该设计模式的说明,设计相应的子类型继承关系树,然后在具体应用中通过为一个 CommonIntervalSet 对象逐层装饰三个不同特征,即可实现应用所需的组合特征。
3.2.3 面向各应用的IntervalSet子类型设计(个性化特征的设计方案)
基于上一小节的设计方案6,分别构造/派生出更具体的、面向应用的 ADT 子类型,每个子类型可根据应用需要来选择是基于 IntervalSet派生还是基于 MultiIntervalSet派生。
面向值班表管理APP:
面向值班表的应用是基于IntervalSet派生的,其相应的ADT如下:
在定义了ADT之后,我们可以根据IntervalSet,派生出相关的一些操作:
判断时间表是否已经安排满 public boolean isFull()
判断是否为空 public boolean isBlankTable()
判断是否为子区间 private boolean isSubSection
获得所有的空时间段 public IntervalSet getEmptySet()
插入一个标签 public void insertToTable
删除所有含有目标标签点的条目 public boolean deleteLabel(L label)
获得时间表中所有标签 public Set allLabels()
根据时间查询标签信息 public L getLabelWithTime
当然,由于是继承类型,我们需要将方法重写,由于函数数量太多,这里仅列出一个函数的分析,其余的与之同理,不进行赘述。
有了这些函数之后,再把这些函数应用于App所需要的操作中,就可以比较容易的实现操作(详情见代码附件)
最终测试结果:
APP运行截图:
3.3 面向可复用性和可维护性的设计:MultiIntervalSet
3.3.1 MultiIntervalSet的共性操作
创建一个空对象 empty()
创建一个非空对象: MultiIntervalSet(IntervalSet initial)
在当前对象中插入新的时间段和标签 void insert(long start,
long end, L label)
获得当前对象中的标签集合 Set labels()
从当前对象中移除某个标签所关联的时间段 boolean remove(L
label)
返回某个标签对应的时间段的开始时间 long start (L label)
返回某个标签对应的时间段的结束时间 long end (L label)
3.3.2 局部共性特征的设计方案
使用方案6来完成局部共性特征的设计,将 CommonIntervalSet 看作是原始的、未被装饰的对象,将这三个维度看 作是三种“装饰”(每个维度的不同特征取值可以产生不同的“装饰”效果)。参照讲义上关于该设计模式的说明,设计相应的子类型继承关系树,然后在具体应用中通过为一个 CommonIntervalSet 对象逐层装饰三个不同特征,即可实现应用所需的组合特征。
3.3.3 面向各应用的MultiIntervalSet子类型设计(个性化特征的设计方案)、
(1)面向进程管理APP 的应用:
对其ADT定义如下:
函数分别是
public long getProcessID()
public String getProcessName()
public long getMinRunTime()
public long getMaxRunTime()
public boolean equals(Object o)
public String toString()
当然,由于是继承类型,我们需要将方法重写,由于函数数量太多,这里仅列出一个函数的分析,其余的与之同理,不进行赘述。
有了这些函数之后,再把这些函数应用于App所需要的操作中,就可以比较容易的实现操作(详情见代码附件)
最终测试结果
程序运行截图:
(2)面向课程管理系统:
对其ADT定义如下:
函数分别定义为:
public String getCourseName() {
return name;
}
public String getCourseTeacher() {
return teacher;
}
public String getCourseLocation() {
return location;
}
public long getCourseWeekTime() {
return week;
}
public String getCourseSection() {
return section;
}
@Override
public boolean equals(Object o)
public int hashCode()
测试结果为:
程序运行截图: