一、实验内容及要求
【问题描述】
参加计算机设计大赛的n个学校编号为1-n,赛事分成m个项目,项目的编号为1-m.比赛获奖按照得分降序,取前三名,写一个统计程序产生各种成绩单和得分报表。
【基本要求】
- 每个比赛项目至少有10支参赛队;每个学校最多有6支队伍参赛;
- 能统计各学校的总分;
- 可以按照学校编号或名称查询,学校的总分、各项目的总分排序输出;
- 可以按学校编号查询学校某个项目的获奖情况;可以按项目编号查询取得前三名的学校;
- 数据存入文件并能随时查询
- 每个学校的每个参赛队伍只能参加一个赛事项目
【设计要求】
- 输入数据形式和范围:可以输入学校的名称,赛事项目的名称。
- 输出形式:有中文提示,各学校分数为整数
- 界面要求:交互设计要合理,每个功能可以设立菜单,根据提示,完成相关功能的要求。
- 存储结构:学生自己根据系统功能要求自己设计,但是赛事相关数据要存储在文件中。
【测试数据】
要求使用全部合法数据,整体非法数据,局部非法数据。进行程序测试,以保证程序的稳定。
【实现提示】
假设3<赛事项目数量<=10,学校名称长度不超过20个字符。每个赛事结束时,将其编号、名称输入,并依次输入参赛学校编号、学校名称和成绩。
二、问题分析
概述
- 本实验的编程语言采用Java,并用Swing实现了GUI,可以实现学校、项目的录入,学校参加项目及成绩的录入、查询学校的成绩及所获奖项、查询某一项目所有参赛学校的排名情况等功能。
- 对于数据结构的设计,很显然本题是一个多对多关系,每个项目有不同的学校参加,同样的,每个学校可以参加不同的项目。所以除了学校类和项目类之外,还需要建立学校与项目之间关系的第三方表来实现查询和排名。所有表都是采用顺序表进行存储。
- 本程序数据的持久化采用了apache的POI实现了学校、项目以及记录的读入和写出。POI实现了程序与后台excel文件的交互,可以随时通过excel文件查询所有记录。
表的添加、查找和排序
该部分就是顺序表的增加、查找以及排序,不过程序需要将第三方记录表与学校表和项目表联系在一起。在添加记录时需要判断学校表以及项目表中是否存在这个学校或项目,同时也要加上题目的要求:如每个学校最多6支队伍等。
下图是三个表之间的关系
三、逻辑设计
程序有三个包分别为domain、 service、 ui
domain包下提供了三个类:
- School类:包含了学校的私有属性:编号id、学校的名称name。提供了setter和getter并重写了equals方法用于查找时的比较。
- Event类:包含了项目的私有属性:编号id、项目的名称那么。提供了setter和getter并重写了equals方法用于查找时的比较。
- Record类:它代表着第三方表的一行,包含私有属性:学校id、学校名称、项目id、项目名称、得分以及获奖情况。
service包下提供了六个类:
- ExcelReader类:用于实现程序从Excel读取数据到内存中。
- ExcelWriter类:用于实现将程序运行时的信息实时写入到Excel文件中,提供了数据添加的写入、获奖情况评定的写入、排序筛选的写入等。
- EventService类:用于实现对项目的查找和添加。
- SchoolService类:用于实现对学校的查找和添加。
- RecordService类:该类是程序的核心,用于实现对每一条记录的匹配、添加、查找、排序、评定获奖情况等功能。
- ServiceException类:该类是自定义异常处理类,接收程序中所有出现的异常:如输入序号和名称不匹配问题等。并通过getMessage方法实时反映到用户图形化界面中。
ui包是Swing的一些组件类,用于实现GUI,同时其中的MainInterface类也是程序的入口。
以下是程序的流程图:
四、物理设计
学校、项目以及记录的数据结构都采用了顺序表:
有关学校的操作:
package com.sevice;
import com.domain.School;
import java.io.IOException;
import java.util.List;
public class SchoolService {
private List<School> schoolList;
//构造方法:读入各学校数据
public SchoolService() {
try {
schoolList = new ExcelReader().readSchool("Data/学校信息.xls");
} catch (IOException e) {
e.printStackTrace();
}
}
//获取学校表
public List<School> getSchoolList() {
return schoolList;
}
//添加一个学校
//判断学校名字是否超出20个字符
//判断是否存在id、Name重名的情况
public void addSchool(int id, String Name) throws ServiceException {
//判断学校的名字是否超出20个字符
if (Name.length()>20){
throw new ServiceException("学校的名称不能超过20个字符");
}
//判断是否存在重名的情况
for (School school: schoolList){
if (school.getSchoolId()==id){
throw new ServiceException("已经有编号为" + id +"的学校了");
}
if (school.getName().equals(Name)){
throw new ServiceException("已经有名称为" + Name + "的学校了");
}
}
//将其加入学校表中
School school = new School(id,Name);
schoolList.add(school);
//更改学校表文件
ExcelWriter excelWriter = new ExcelWriter();
try {
excelWriter.writeSchool("Data/学校信息.xls",school);
} catch (IOException e) {
e.printStackTrace();
}
}
}
有关项目的操作:
package com.sevice;
import com.domain.Event;
import java.io.IOException;
import java.util.List;
/*
EventService类:提供对各项赛事项目的操作
*/
public class EventService {
//存放各项目的顺序表
private List<Event> eventList;
//构造方法:读入各项目数据
public EventService() {
//读入项目表文件,将数据新建成event对象后放入eventList中
try {
eventList = new ExcelReader().readEvent("Data/项目信息.xls");
} catch (IOException e) {
e.printStackTrace();
}
}
//获取项目表
public List<Event> getEventList() {
return eventList;
}
//添加一个比赛项目
//判断是否存在id、Name重名的情况
public void addEvent(int id, String Name) throws ServiceException {
//判断是否存在重名的情况
for (Event event : eventList) {
if (event.getEventId() == id) {
throw new ServiceException("已经有编号为" + id + "的项目了");
}
if (event.getName().equals(Name)) {
throw new ServiceException("已经有名称为" + Name + "的项目了");
}
}
//将其加入项目表中
Event event = new Event(id, Name);
eventList.add(event);
//更改项目表文件
ExcelWriter excelWriter = new ExcelWriter();
try {
excelWriter.writeEvent("Data/项目信息.xls", event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
有关记录表的操作:
package com.sevice;
import com.domain.Event;
import com.domain.Record;
import com.domain.School;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/*
RecordService类:提供一个记录表,存放各个学校和项目的记录
*/
public class RecordService {
private List<Record> recordList;
//构造方法读入各记录数据
public RecordService() {
try {
recordList = new ExcelReader().readRecord("Data/记录信息.xls");
} catch (IOException e) {
e.printStackTrace();
}
}
//获取赛事记录表
public List<Record> getRecordList() {
return recordList;
}
//添加一条记录(这里不添加获奖名次)到记录表中
//判断是否有该学校和项目
//判断该学校是否超出六支队伍
//判断一个学校是否多次参加了同一个项目
public void addRecord(int schoolId, String schoolName, int eventId, String eventName, int score) throws ServiceException {
//获得项目表和学校表
List<Event> eventList = new EventService().getEventList();
List<School> schoolList = new SchoolService().getSchoolList();
//需要添加的学校和项目
School school = new School(schoolId, schoolName);
Event event = new Event(eventId, eventName);
boolean hasEvent = false; //布尔标记:是否有该项目
boolean hasSchool = false; //布尔标记:是否有该学校
//判断是否有该学校和项目
for (Event event1 : eventList) {
if (event1.equals(event)) {
hasEvent = true;
break;
}
}
for (School school1 : schoolList) {
if (school1.equals(school)) {
hasSchool = true;
break;
}
}
if (!hasSchool) {
throw new ServiceException("学校表中查无此学校,请检查输入的学校id是否与学校名称匹配");
}
if (!hasEvent) {
throw new ServiceException("项目表中查无此项目,请检查输入的项目id是否与项目名称匹配");
}
//程序能执行到这里说明有该学校和项目
//判断该学校是否超出六只队伍以及该学校是否多次参加了同一个项目
int count = 0; //计数器
for (Record record : recordList) {
//获得记录中的学校和项目
School RSchool = record.getSchool();
Event REvent = record.getEvent();
if (RSchool.equals(school)) {
count++;
}
//如果学校和项目都相等,说明该学校已经参加了这个项目
if (RSchool.equals(school) && REvent.equals(event)) {
throw new ServiceException("该学校已经参加过此项目,不能重复参加");
}
}
//如果计数器大于等于六,说明该学校已经参加过6个项目了
if (count >= 6) {
throw new ServiceException("一个学校最多参加六个项目,该学校已经参加过6个项目");
}
//程序能执行到这里说明该记录是可以添加的
//把该记录添加到记录表中
Record record = new Record(school, event, score, null);
recordList.add(record);
//更改记录表文件
ExcelWriter excelWriter = new ExcelWriter();
try {
excelWriter.writeRecord("Data/记录信息.xls", record);
} catch (IOException e) {
e.printStackTrace();
}
}
//查询某一项目的排名情况
//传入项目id和项目名称
//返回该项目降序的记录表,并修改前三名的获奖情况
//这一方法修改了原项目表以及后台文件
public List<Record> queryRankOfEvent(int eventId, String name) throws ServiceException {
//传入的项目
Event event = new Event(eventId, name);
//最后的排名结果
List<Record> records = new ArrayList<>();
//查询项目表中是否有该项目,如果没有,抛出异常
EventService eventService = new EventService();
List<Event> eventList = eventService.getEventList();
boolean hasEvent = false;
for(Event event1:eventList ){
if (event1.equals(event)) {
hasEvent = true;
break;
}
}
if (!hasEvent){ //没有该项目就抛出异常
throw new ServiceException("项目表中没有该项目,请检查输入的项目编号是否与项目名称匹配");
}
for (Record record : recordList) {
if (record.getEvent().equals(event)) {
//如果是这个项目,就把它加入
records.add(record);
}
}
//将得到的记录表排序
records.sort(new Comparator<Record>() {
@Override
//按照成绩降序排列
public int compare(Record o1, Record o2) {
return Integer.compare(o2.getScore(), o1.getScore());
}
});
//如果这个项目参赛队伍小于10支,那么抛出异常
if (records.size() < 10) {
throw new ServiceException("项目编号为" + eventId + "的参赛队伍数量小于十支,无法评定排名和获奖情况");
}
//修改在该项目的记录表中的获奖情况
Record record1 = records.get(0);
record1.setAward("一等奖");
records.set(0, record1);
Record record2 = records.get(1);
record2.setAward("二等奖");
records.set(1, record2);
Record record3 = records.get(2);
record3.setAward("三等奖");
records.set(2, record3);
//修改在原记录表的获奖情况
for (Record record : recordList) {
if (record.equals(record1)) {
recordList.set(recordList.indexOf(record), record1);
} else if (record.equals(record2)) {
recordList.set(recordList.indexOf(record), record2);
} else if (record.equals(record3)) {
recordList.set(recordList.indexOf(record), record3);
}
}
//修改后台文件
ExcelWriter excelWriter = new ExcelWriter();
try {
excelWriter.changeScore("Data/记录信息.xls", records);
} catch (IOException e) {
e.printStackTrace();
}
//返回该项目排序表
return records;
}
//成绩和获奖评定,该方法修改了原项目表和后台文件的所有获奖信息
// 程序需要执行此方法之后才能查询每个学校的获奖情况
public void setAward() throws ServiceException {
EventService eventService = new EventService();
//获得项目表
List<Event> eventList = eventService.getEventList();
for (Event event : eventList) {
//添加有关每一个项目的获奖情况
queryRankOfEvent(event.getEventId(), event.getName());
}
}
//查询某一个学校的记录、及其获奖情况
//传入某个学校的id和学校名称
//排序并返回该学校的所有记录
public List<Record> queryRecordOfSchool(int schoolId, String schoolName) throws ServiceException {
//传入的学校
School school = new School(schoolId, schoolName);
//需要返回的表
List<Record> records = new ArrayList<>();
for (Record record : recordList) {
//如果记录表中学校与传入的学校相等,那么加入返回表中
if (record.getSchool().equals(school)) {
records.add(record);
}
}
//如果records为空,那么抛出异常
if (records.isEmpty()){
throw new ServiceException("没有该学校的记录,请检查输入学校的id是否和学校名称匹配,并确认该学校是否参加了比赛");
}
//将得到的记录表排序
records.sort(new Comparator<Record>() {
@Override
//按照成绩降序排列
public int compare(Record o1, Record o2) {
return Integer.compare(o2.getScore(), o1.getScore());
}
});
return records;
}
//查询一个学校的总分
//传入某一个学校的id,学校的名称
//返回该学校的总分
public int queryScoreOfSchool(int schoolId, String schoolName) throws ServiceException {
//获得该学校的所有记录
List<Record> records = queryRecordOfSchool(schoolId, schoolName);
int score = 0;
for (Record record : records) {
score += record.getScore();
}
return score;
}
}
总结
对于本次实验来说还有许多不足之处:
- 学校和项目的信息用户是无法直接通过图形化界面删除或修改的,只能添加,想要删除或修改需要到Excel表格中修改。其实程序可以提供删除或修改记录的功能。
- 一旦评定获奖情况后,不能再添加学校、项目或记录,因为这时候Excel文件已经被修改,程序没有提供还原获奖情况的功能,想要重新评定获奖情况只能到Excel表格中修改,并重新刷新程序。
- 该程序没有对同分的情况进行处理,如果同一项目不同学校之间分数相同,程序会按照报名的次序(添加到记录表中的先后)来优先评定较高的名次。