有了出题策略和试卷就可以进行考试了。下面用类图4-16说明考试模块的主要类。
图4-16 考试模块类图
该图描述了考试相关的属性和方法。首先看看Examinatinon类。该类表示一次考试。考试有开始时间StartTime、结束时间EndTime、迟到时间LateMinutes。迟到的学生不准参加考试。PermitSubmitMinutes表示允许交卷的最迟时间。IsStudentLate方法判断学生是否迟到。IsStudentEarly判断学生是否提前进考场。IsPermitSubmit判断学生是否可以交卷。IsExaminationOver判断考试是否结束。IsReEnterExamination判断学生是否交卷后又参加同一考试。IsInAttendClasses判断学生是否在考试允许参加的班级之中。EnterExamination表示进入考场。该方法会检查所以要用到的考试规则。如果学生允许进入考场。则根据考试相关联的PaperStrategy对象为学生生成Paper对象并将Paper对象的IsSubmit属性设为false表示未交卷的Paper对象。然后将Paper对象分别加入到考试Examination对象和学生对象的Papers集合中。Student对象的GetCurrentPaper方法返回学生Paper集合中IsSubmit为false的Paper。表示学生当前正在做的试卷对象。如果为空说明学生没有参加过考试或者已经交卷。下面给出Examination类的主要的方法代码来说明是如何实现的。
{
public class Examination:DomainObject<long>
{
public void EnterExamination(Student student)
{ //首先判断学生是否因为掉线而重新进入考场如果是则不做任何操作直接返回
if (student.CurrentPaper != null&& student.CurrentPaper.Examination==this)
return;
if (IsExaminationOver())
throw new Exception("考试已经结束!");
if (!AttendClasses.Contains(student.Class))
throw new Exception("学生不在参加考试班级");
if (IsStudentLate())
throw new Exception("学生考试迟到不能参加考试");
if (IsStudentEarly())
throw new Exception("考试未开始");
if (IsReEnterExamination())
throw new Exception("已经参加过本次考试");
if (student.CurrentPaper == null)
{
student.CurrentPaper = PaperStrategy.GetPaper();
student.CurrentPaper.Student = student;
student.CurrentPaper.Examination = this;
this.Papers.Add(student.CurrentPaper);
}
}
public void SubmitPaper(Student student)
{
if (student.CurrentPaper == null)
throw new Exception("考试没有在考试状态或已经交卷!");
if (!IsPermitSubmit())
throw new Exception("不到交卷时间");
student.CurrentPaper.IsSubmited = true;
}
private bool IsStudentLate()
{
TimeSpan timeSpan =DateTime.Now - StartTime;
if (timeSpan.Minutes > lateMinutes)
return true;
else
return false;
}
private bool IsStudentEarly()
{
TimeSpan span = StartTime - DateTime.Now;
if(span.TotalSeconds>0)
return true;
else
return false;
}
private bool IsPermitSubmit()
{
TimeSpan span = DateTime.Now - StartTime;
if (span.TotalMinutes > PermitSubmitMinutes)
return true;
else
return false;
}
public bool IsExaminationOver()
{
TimeSpan span = DateTime.Now - EndTime;
if (span.TotalMinutes > 0)
return true;
else
return false;
}
private bool IsReEnterExamination(Student student)
{
if (student.CurrentPaper == null)
{
foreach (Paper paper in student.Papers)
{
if (paper.Examination == this)
return true;
}
}
return false;
}
}
}
下面是Student的CurrentPaper属性
public virtual Paper CurrentPaper
{
get
{
foreach (Paper paper in Papers)
{
if (paper.IsSubmited == false)
return paper;
}
return null;
}
set
{
if (CurrentPaper != null)
throw new Exception("已经有了试卷");
Papers.Add(value);
}
}
以上代码展现了考试部分主要的逻辑实现。当违反考试规则的时候我们就抛出一个异常并将具体的规则信息作为异常参数。在上层的代码使用中可以将对EnterExamination方法的调用放到try语句块中并在catch语句中捕获异常并将消息显示给用户。伪码如下
{
examination.EnterExamination(student);
}
catch (Exception ex)
{
messageLabel.Text = ex.Message;
}
这种通过异常来报告错误信息的方式使代码的更加的清晰易于维护。因为将出现异常和处理异常的代码很好地进行了分离。这样当异常处理比较繁琐时候业务逻辑代码才不会淹没在异常处理代码当中。