哈工大2020春软件构造Lab3实验报告

实验报告只是为了提供给学弟学妹们参考,所以很多代码都没有完整给出,希望学弟学妹们只用来参考,请勿直接抄袭!!!
有问题请联系QQ:1187987704

1 实验目标概述

本次实验覆盖课程第 3、4、5 章的内容,目标是编写具有可复用性和可维护
性的软件,主要使用以下软件构造技术:
⚫ 子类型、泛型、多态、重写、重载
⚫ 继承、代理、组合
⚫ 常见的 OO 设计模式
⚫ 语法驱动的编程、正则表达式
⚫ 基于状态的编程
⚫ API 设计、API 复用
本次实验给定了五个具体应用(高铁车次管理、航班管理、操作系统进程管
理、大学课表管理、学习活动日程管理),学生不是直接针对五个应用分别编程
实现,而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其实现,充
分考虑这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)
和更容易面向各种变化(可维护性)。

2 实验环境配置

前面实验已经配置完备,所以没有问题

在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
https://github.com/ComputerScienceHIT/Lab3-1180300120.git

3 实验过程

请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。

3.1 待开发的三个应用场景

列出你所选定的三个应用。
我选择的是125三个场景,分别是航班,高铁,学习日程

分析三个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
位置:可以使用同一类来创建
资源:三者资源共性不大
时间:三者都可以通过时间对来表示时间并且都可提前设定,只不过高铁需 要一个时间对的list
状态转换中:只有高铁可以阻塞,其余状态一样,所以可以单独为高铁设置 一个状态的阻塞功能即可
位置更改:只有学习日程可以更改位置,所以可以单独为其设置此功能
以下为我在写代码前初步构建的一个框图,虽然不是很完备,但是可以体现 初步的框架:

在这里插入图片描述

3.2 面向可复用性和可维护性的设计:PlanningEntry

该节是本实验的核心部分。

3.2.1 PlanningEntry的共性操作

状态转换有5个:构建(Waiting),分配(Allocate),启动(Start),取消(Cancel), 结束(Finish)
获取计划项的名字:getName()
获取计划项的状态名:String getState()
获取计划项的状态:State getRealState()
获取计划项的类型:String getType()

3.2.2 局部共性特征的设计方案

对于局部的共性特征,我采用了方案5给出的方法,采用委托的形式,首先建立一些接口分别对应不同的功能,有:OneLocationEntry,TwoLocationEntry, MultipleLocationEntry, OneSourceEntry, MultipleSortedResourceEntry, MultipleNotSortedResourceEntry, ChangeableLocationEntry, BlockedEntry等接口,然后对每个进行一个实现,在接下来实现对应的计划项时就可以通过这些接口的组合来实现了,比如航班的接口需要继承TwoLocationEntry, OneSourceEntry, 高铁接口继承MultipleLocationEntry, BolckedEntry, MultipleSortedResourceEntry, 学习日程接口继承OneLocationEntry, MultipleNotSortedResourceEntry, ChangeableLocationEntry,这样通过这些接口组合就可以实现很多不同的计划项了,算是一个比较大的复用。

3.2.3 面向各应用的PlanningEntry子类型设计(个性化特征的设计方案)

对于航班:上面那些通过接口继承的功能,再加上继承了CommonPlanningEntry类后功能基本都已经实现,只是时间和航班名称还没有处理,所以我建立一个TimeSlot时间对来储存起止时间,然后建立一个String name来储存航班名称,并且写其getter方法

对于高铁:上面那些通过接口继承的功能,再加上继承了CommonPlanningEntry类后功能基本都已经实现,只是时间和高铁名称还没有处理,所以我建立了一个List用来储存各个段的起止时间,然后建立一个String name来储存高铁名称,并且写其getter方法

对于学习日程:上面那些通过接口继承的功能,再加上继承了CommonPlanningEntry类后功能基本都已经实现,只是时间和学习活动名称还没有处理,所以我建立一个TimeSlot时间对来储存起止时间,然后建立一个String name来储存学习活动名称,并且写其getter方法

从这里开始已经体现出来了复用的好处,这一小节中基本没有重新写太多的方法,都是通过委托机制来委托之前已经写好的方法来实现的,所以这一节比较轻松

3.3 面向复用的设计:R

R主要是资源,所以我先建立一个Resource的接口,由于三种资源差异较大,所以分别实现三个资源实例类
FlightResource:包括飞机编号,飞机型号,飞机座位数,飞机机龄
TrainResource:包括车厢编号,车厢型号,车厢座位数,车厢出厂年份
ActivityResource:包括资料名,发布部门,发布时间

3.4 面向复用的设计:Location

Location是可以完全复用的,所有的Location都是包括:地点名,地点经度,地点纬度,是否可以共用标识,但是要注意的是在3.13节中输入的地点信息没有经纬度,所以在读入时我都默认设为了-1代表未设置经纬度

3.5 面向复用的设计:Timeslot

TimeSlot这个类同样是完全复用的,这个类中要储存的信息比较多,根据读入的起止时间(yyyy-MM-dd HH:mm)格式,要将其拆分出年份,月份,日,小时,分钟,分别储存起来,再加上输入的yyyy-MM-dd HH:mm,起止都算上共有12个信息,并且都有对应的getter,还有就是为了方便后面的比较,我利用Java自带的Date将其转为long型

3.6 面向复用的设计:EntryState及State设计模式

State有两种情况,但是可以直接设计复杂的那一种,然后将block在不用的时候不体现(用户也调用不了)就可以了,我的state模式建立了6个状态类,一个接口,然后分别在每个状态类中实现就可以了,比如对于某些状态只能转移到特定的状态,那么其他的状态转移就是无效的并且提示Wrong的信息

3.7 面向应用的设计:Board

Board可以说是比较麻烦的了,要更具当前的时间先将需要展示的计划项挑选出来,然后对其按先后时间排序,最后即可利用排好序的应该显示的list将所有计划项显示出来,对于飞机和高铁其中需要一个list用来存储经过此地点的计划项,一个list用来存储即将在一小时内到达的计划项(利用前面Time中转的long型来判断一小时=60601000ms),一个list用来存储即将在一小时内出发的计划项,对于学习日程,需要判定是当天的计划项,所以用到我们之前Time中的获取起始年份,月份,日,这三项判定即可,其余操作同飞机与高铁没有太大区别

3.8 Board的可视化:外部API的复用

可视化我利用的是Java自带的JTable,对JTable的学习并不是很难,因为这个可视化没有需要太多复杂的操作,只需要了解基础的操作就可以,主要是要仔细地设计,让抵达和出发板块清晰明朗,然后确定好二维数组哪里该填什么之后,逐个录入信息即可

3.9 可复用API设计及Façade设计模式

这个模块需要大量的比较,但是具体的思路不是很难,我都是通过instance of或者我之前在CommonPlanningEntry中写好的getType来判定是三种计划项中的哪一种再分别操作的

3.9.1 检测一组计划项之间是否存在位置独占冲突

对于航班和高铁,这个直接返回false即可,代表没有冲突
对于学习日程,需要2重循环遍历list,如果两个计划项的location为同一个,那么对其起止时间进行判定,下面用src1-dst1和src2-dst2代表两个计划项的起止时间,如果src1在src2和dst2之间或者dst1在src2和dst2之间,那么返回true,代表有冲突,如果在此期间没有返回,那么最后返回false代表没有冲突

3.9.2 检测一组计划项之间是否存在资源独占冲突

对于学习日程,这里直接返回false,代表没有冲突
对于航班,利用2重循环遍历list,如果两个计划项的Resource相同,那么对其起止时间进行判定,下面用src1-dst1和src2-dst2代表两个计划项的起止时间,如果src1在src2和dst2之间或者dst1在src2和dst2之间,那么返回true,代表有冲突,如果在此期间没有返回,那么最后返回false代表没有冲突
对于高铁,利用4重循环,外2重,内2重,外面的2重循环是将计划项两两选取,内2重循环,用来遍历两个计划项的每节车厢,如果有两个计划项有相同车厢,那么对其起止时间进行判定,下面用src1-dst1和src2-dst2代表两个计划项的起止时间,如果src1在src2和dst2之间或者dst1在src2和dst2之间,那么返回true,代表有冲突,如果在此期间没有返回,那么最后返回false代表没有冲突

3.9.3 提取面向特定资源的前序计划项

对于学习日程,直接返回null,因为此功能不适用
对于航班,遍历list,如果计划项和传入的计划项使用资源相同,那么需要对起止时间进行判定,传入计划项用src1-dst1表示,当前遍历到的src2-dst2表示,在满足dst2<=src1的所有计划项中选取dst2最大的计划项然后返回,选取最晚的计划项的操作很简单,这里不做详细说明
对于高铁,遍历list,如果当前遍历到的计划项的车厢中有一个是传入的车厢资源,那么需要对起止时间进行判定,传入计划项用src1-dst1表示,当前遍历到的src2-dst2表示,在满足dst2<=src1的所有计划项中选取dst2最大的计划项然后返回,选取最晚的计划项的操作很简单,这里不做详细说明

3.10 设计模式应用

请分小节介绍每种设计模式在你的ADT和应用设计中的具体应用。

3.10.1 Factory Method

工厂模式即用户不需要知道具体的计划项类的类名,直接可以返回一个相应的计划项,那么就是利用可以区分三种计划项的名字来供用户使用,然后传入对应参数,即可得到相应的计划项类

3.10.2 Iterator

迭代器模式,我将需要迭代的计划项都放到一个特定的list中,然后需要遍历时,直接获取迭代器即可,即返回的是特定的list的迭代器

3.10.3 Strategy

策略模式,我对于APIs建立两个实例类,一个为PlanningEntryAPIsFirst,另一个为PlanningEntryAPIsSecond,是检测地址冲突的方法采用了不同的算法,具体算法在这里不详细说明,写好之后,用户可以通过选择采用哪种策略,然后调用不同的APIs进行实施

3.11 应用设计与开发

利用上述设计和实现的ADT,实现手册里要求的各项功能。
只需保留你选定的三个应用即可。
使用说明:我在设计时,除了3.13文件读入航班计划项外(3.13在FlightScheduleAPP中直接选择16功能外部读入即可),其他都应满足以下规则:在创建计划项之前,必须要有已经建立好的相应资源和相应地点,否则会抛出异常导致计划项建立失败,建立计划项时必须将资源也一同分配好,因为要求计划项一旦创建资源不可变,那么如果可以后分配资源,可能会导致资源变为可变,所以我在设计时不允许先建立计划项后分配资源

3.11.1 航班应用

航班应用,功能有:

String function = 	"0.退出\n" +
							"1.增加可用资源\n" + 
							"2.删除可用资源\n" +
							"3.增加可用位置\n" +
							"4.删除可用位置\n" +
							"5.增加一条新的计划项\n" +
							"6.取消某个计划项\n" +
							"7.为某个计划项分配资源\n" +
							"8.结束某个计划项\n" +
							"9.查看某个计划项的当前状态\n" +
							"10.检测计划项中资源冲突\n" +
							"11.列出使用某资源的所有计划项\n" +
							"12.查询某计划项的前序计划项\n" +
							"13.选定位置并展示信息板\n" +
							"14.启动某个计划项\n" +
							"15.查看功能列表\n";

对于1.询问相应信息后,建立相应FlightResource,将其存放在资源list中
对于2.询问相应信息后,在资源list中寻找对应的资源,如果有将其删除,并将所有使用此资源的计划项一起删除,因为资源不存在了,计划项自然也不存在了

对于3.询问相应信息后,建立相应的Location,将其存放在地点list中,然后建立对应board
对于4.询问相应信息后,在地点list中寻找相应位置,如果找到将其删去,并将所有使用此地点的计划项一起删除,因为地点不存在了,计划项自然也不存在了
对于5.询问相应信息后,分别查看资源和地点是否已经存在,如果不存在,抛出异常无法建立计划项,如果都存在,那么建立新的计划项,然后存放在计划项list中
对于6.询问相应信息后,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,那么对其cancel操作,如果返回false提示计划项的当前状态不可被取消
对于7.我的计划项在建立时就已经分配资源了,因为计划项应该为不可变类型,所以资源不可以后续设置
对于8.询问相应信息后,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,那么对其finish操作,如果返回false提示计划项的当前状态不可被结束
对于9.询问相应信息后,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,则输出计划项的状态
对于10.询问相应信息,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,询问选择策略1或2,然后对应的使用APIs处理即可
对于11.询问相应资源信息,然后在资源list中寻找资源,如果不存在,则提示信息资源不存在,如果存在,那么遍历计划项list,如果有使用此资源的计划项,则输出相应信息
对于12.询问相应信息,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,询问选择策略1或2,然后对应的使用APIs处理即可
对于13.询问相应地点信息,在board的list中找到对应board,然后调用其可视化方法即可
对于14.询问相应信息后,查看计划项是否存在,如果不存在,提示信息不存在计划项,如果存在,那么对其start操作,如果返回false提示计划项的当前状态不可被开始
对于15.直接打印function的功能字符串即可

3.11.2 高铁应用

高铁应用,功能有:

String function = 	"0.退出\n" +
				"1.增加可用资源\n" + 
				"2.删除可用资源\n" +
				"3.增加可用位置\n" +
				"4.删除可用位置\n" +
				"5.增加一条新的计划项\n" +
				"6.取消某个计划项\n" +
				"7.为某个计划项分配资源\n" +
				"8.结束某个计划项\n" +
				"9.查看某个计划项的当前状态\n" +
				"10.检测计划项中资源冲突\n" +
				"11.列出使用某资源的所有计划项\n" +
				"12.查询某计划项的前序计划项\n" +
				"13.选定位置并展示信息板\n" +
				"14.启动某个计划项\n" +
				"15.阻塞(停靠到经停站)某个计划项\n" +
				"16.查看功能列表\n";

所有功能与航班的几乎一样,不再重复说明,只不过在创建计划项时需要先询问一共有几站,然后对应次数的询问信息即可

3.11.3 学习活动应用

学习活动应用,功能有:

String function = 	"0.退出\n" +
							"1.增加可用资源\n" + 
							"2.删除可用资源\n" +
							"3.增加可用位置\n" +
							"4.删除可用位置\n" +
							"5.增加一条新的计划项\n" +
							"6.取消某个计划项\n" +
							"7.为某个计划项分配资源\n" +
							"8.结束某个计划项\n" +
							"9.查看某个计划项的当前状态\n" +
							"10.检测计划项中资源冲突\n" +
							"11.列出使用某资源的所有计划项\n" +
							"12.查询某计划项的前序计划项\n" +
							"13.选定位置并展示信息板\n" +
							"14.启动某个计划项\n" +
							"15.更改活动位置\n" +
							"16.查看功能列表\n";

所有功能与航班的几乎一样,不再重复说明

3.12 基于语法的数据读入

修改“航班”应用以扩展该功能。
正则表达式为

Flight:(.*?),(.*?)\n\\{\nDepartureAirport:(.*?)\nArrivalAirport:(.*?)\nDepatureTime:(.*?)\nArrivalTime:(.*?)\nPlane:(.*?)\n\\{\nType:(.*?)\nSeats:(.*?)\nAge:(.*?)\n\\}\n\\}\n

然后读入过程中需要随时检测实验手册中提到的冲突,如果遇到冲突则抛出异常,没有冲突的话,建立对应的资源,地点,board,计划项,再分别放到对应的list中

3.13 应对面临的新变化

只考虑你选定的三个应用的变化即可。

3.13.1 变化1

航班的变化是三个中最大的,需要设置经停站,在经停时属于阻塞状态,所以还要设为可阻塞,我为MultipleLocationEntry建立了一个新的实例类MultipleLocationEntryImp2,因为要保证最多只有3个地点,相应的FlightEntry中也要修改构建函数,传入的应该是MultipleLocationEntryImp2而不是TwoLocationEntryImp1,还有就是之前的航班类用的是TimeSlot,现在应该为List,然后加上阻塞功能即可,board变为和之前高铁那样就好,由于APIs也涉及到航班,所以也需要有相应的改动,但是变化不大,还有就是APP需要有新功能的加入,所以变化是一个新的类加上3个类的改动,总体感觉复用性还是不错的,因为不需要花费很大的时间去改动,复用性很高,代价比较小

3.13.2 变化2

高铁的变化是比较小的,只需要在cancel的时候判定一下是否为allocate的状态,如果是,则返回false,并提示不可取消,如果不是,正常按照之前设计好的状态转移进行即可,只有TrainEntry发生了小变化,所以复用性极高,代价很小

3.13.3 变化3

学习日程的变化就是借口多继承一个BlockableEntry接口即可,而且这个接口之前就有,只需要在ActivityEntry中加入对应功能即可,同时在APP中也要有阻塞的功能选项,所以变化是2个类的小修改,复用性极高,代价很小

3.14 Git仓库结构

请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚314change分支和master分支所指向的位置。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值