软件构造——实验三中的可复用性和可维护性
软件构造的lab3中有三个场景:
(1)值班表管理:安排员工值班,每天只能安排一个员工,且不能出现无人值班的情况。需要每天记录当天值班员工的信息。
(2)操作系统进程调度管理:单核CPU执行多个进程,每个时间只能有一个进程在执行,一个进程的执行可以被分为多个时间段,CPU可以闲置。
(3)大学课表管理:各周课表完全一样,进行周期性的重复,一个时间段内可以安排不同的课程,可以有时间段没课,一个教室可以承担课表中的多门课程。
共性:三个场景都需要在一段时间内执行一定的任务。
特性:场景1不能出现空白时间段,场景3在同一时间内可以有不同的任务进行。
它们都需要用到实现给定的接口IntervalSet的类CommonIntervalSet或是MultiIntervalSet。
我们需要实现以下几个方法:
public void insert(long start, long end, L label) throws IntervalConflictException;
public Set<L> labels();
public boolean remove(L label);
public long start(L label) throws NoSuchElementException;
public long end(L label) throws NoSuchElementException;
以CommonIntervalSet为例,来看看我是如何实现的:
首先需要构建一个合理的数据结构来存储Set的相关信息,我根据模板的提示,选用了两个Map映射来存储:
private final Map<L, Long> startMap = new HashMap<>();
private final Map<L, Long> endMap = new HashMap<>();
在insert函数中直接插入即可:
startMap.put(label, start);
endMap.put(label, end);
但是考虑到应用时,不能有重叠区域,所以在插入前需要进行判断,并且抛出相应异常:
if (startMap.containsKey(label))
return;
boolean[] interval = new boolean[MAX];
for (L l : labels()) {
long s = startMap.get(l);
long e = endMap.get(l);
for (int i = (int) s; i < e; i++)
interval[i] = true;
}
for (int i = (int) start; i < end; i++)
if (interval[i] == true)
throw new IntervalConflictException("The interval exists.");
剩下几个方法的实现相对简单,就不一一列举了。
然后来看MultiIntervalSet,它相比CommonIntervalSet,多出来一个功能:一个label可以拥有多个时间段。那么之前的Map映射显然是无法满足这个要求了,我还是根据模板,采用了两个List来进行存储(这里用了protected而不是private是为了方便之后扩展其子类,但在一定程度上丢失了其封装性):
protected final List<L> labelList = new ArrayList<>();
protected final List<Long> valueList = new ArrayList<>();
和之前一样,检查完重叠区域后就可以直接插入:
labelList.add(label);
valueList.add(start);
valueList.add(end);
获得label及其时间段的方法是以随机读取方式遍历链表,n代表label,2n和2n+1代表其时间段。
这是intervals方法的实现:
public IntervalSet<Integer> intervals(L label) throws NoSuchElementException, IntervalConflictException {
if(!labelList.contains(label))
throw new NoSuchElementException("No such label.");
IntervalSet<Integer> ans = new CommonIntervalSet<>();
int count = 0;
for(int i = 0; i < labelList.size(); i++) {
if(labelList.get(i).equals(label)) {
ans.insert(valueList.get(2 * i), valueList.get(2 * i + 1), count++);
}
}
return ans;
//throw new RuntimeException("not implemented");
}
以上便是两个可复用可维护的实现了IntervalSet的类的介绍。
实验用例:
对于任务一排班管理系统,我们新建一个DutyIntervalSet作为CommonIntervalSet的子类,并不需要额外添加很多新方法,我这里仅添加了一个判断set是否已满的函数:
public class DutyIntervalSet extends CommonIntervalSet<Employee> {
public DutyIntervalSet() {
}
public boolean noBlank(long max) {
boolean[] duty = new boolean[(int) max];
for(Employee i : labels()) {
for(int j = (int) start(i); j < end(i); j++)
duty[j] = true;
}
for(int i = 0; i < max; i++)
if(!duty[i])
return false;
return true;
}
}
之后便是在操作类DutyRoster中实现各种功能,然后由DutyRosterMenu类显示菜单和读取信息,最后由一个DutyRosterApp类主函数调用运行。
以上为本篇文章的全部内容。
推荐阅读