软件构造 Lab3

2020年春季学期
计算机学院《软件构造》课程

Lab 3实验报告

目录

1 实验目标概述 1
2 实验环境配置 1
3 实验过程 1
3.1 待开发的三个应用场景 1
3.2 面向可复用性和可维护性的设计:PlanningEntry 1
3.2.1 PlanningEntry的共性操作 1
3.2.2 局部共性特征的设计方案 2
3.2.3 面向各应用的PlanningEntry子类型设计(个性化特征的设计方案) 2
3.3 面向复用的设计:R 2
3.4 面向复用的设计:Location 2
3.5 面向复用的设计:Timeslot 2
3.6 面向复用的设计:EntryState及State设计模式 2
3.7 面向应用的设计:Board 2
3.8 Board的可视化:外部API的复用 2
3.9 PlanningEntryCollection的设计 2
3.10 可复用API设计及Façade设计模式 2
3.10.1 检测一组计划项之间是否存在位置独占冲突 2
3.10.2 检测一组计划项之间是否存在资源独占冲突 2
3.10.3 提取面向特定资源的前序计划项 2
3.11 设计模式应用 2
3.11.1 Factory Method 3
3.11.2 Iterator 3
3.11.3 Strategy 3
3.12 应用设计与开发 3
3.12.1 航班应用 3
3.12.2 高铁应用 3
3.12.3 进程应用 3
3.12.4 课表应用 3
3.12.5 学习活动应用 3
3.13 基于语法的数据读入 3
3.14 应对面临的新变化 3
3.14.1 变化1 3
3.14.2 变化2 4
3.14.3 变化3 4
3.15 Git仓库结构 4
4 实验进度记录 4
5 实验过程中遇到的困难与解决途径 4
6 实验过程中收获的经验、教训、感想 5
6.1 实验过程中收获的经验和教训 5
6.2 针对以下方面的感受 5

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

2 实验环境配置
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
https://github.com/ComputerScienceHIT/Lab3-1153470114
2.1 待开发的三个应用场景
选取的火车计划,航班计划,课程计划三个应用场景
选取原因主要为时间都是可以设定的,所以选择了火车计划项,有因为课程计划中只有单个资源和航班计划项中的类似所以选择了课程计划项。相对的可复用性上需要完成的任务较小,可以减少工作量。

2.2 面向可复用性和可维护性的设计:PlanningEntry
2.2.1 PlanningEntry的共性操作
该类是所有计划项的共同类用于后面具体实现进行继承,其中共性的方法有和状态相关方法
//代表一个抽象的计划项对象的实例类,在本项目中,可能代表一辆火车计划,一辆航班计划,或者是一个课程计划
//下面是三种计划共同的状态设计 1:已添加状态 2:已分配状态 3:已运行状态 4.已取消状态 5.已结束状态6.计划阻塞状态
//在该类中构建的方法主要是所有的计划项目都会用到的方法
//可以选择的共性方法有add()添加该项计划allocate()为该项计划分配资源run()启动该项计划cancel()取消该项计划end()
//结束该项计划getTimeslots()获取起止时间setTimeslots(List timeslots)设置起止时间

使用状态模式进行设计,具体的方法实现委托到具体的状态类的对象中实现,同时状态采用单例模式进行设计,同时设计获取项目名称和项目目前状态的方法

对于不同的状态类的设计,该类主要对除了阻塞态的其他状态进行设计考虑

对每一个方法状态方法下进行设计,和打印输出信息,

相关的状态的上下文的设计
由于其中阻塞和恢复两种方法不是所有类所共有的所以在后面设计的针对于火车的类中进行具体的实现。
设计计划的名称属性,和一个timeslot用于记录计划的开始和截止时间,所有的计划都可以共用的属性
局部共性特征的设计方案
2.2.2 面向各应用的PlanningEntry子类型设计(个性化特征的设计方案)
在接口和继承的设计上采用方案4: 定义接口并实现具体类,通过继承树实现各应用在多维度上的不同特征取值的方式,对所需要的接口和类进行逐步的整合形成最后适合于所选择的三项计划的使用需要

针对方案四进行逐步继承的思路的实现分为两类进行逐步继承,火车计划作为一类,分别整合进各类接口有可以阻塞,使用多个资源,计划一旦确定不可更改,计划可以经过多个地点,分别实现过程
可以阻断类的实现

由于可以被阻断所以计划必然会有多个时间段,所以创建相应的list容纳各个时间段
同时添加状态设计的上下文block和recover针对可阻断情况

经过多个地点并可以阻断(继承)

由于经过多个地点所以构建locs的list容纳location地点进行设计,同时设置get和set方法

可以使用多个资源类

设计一个resource的list用来容纳所有需要用到的资源

其中不可改变地点作为一个标志接口,并没有具体的实现方式。
由此完成火车类的组合通过继承的方式对泛型进行擦除完成火车类的设计

综上各类实现的所有方法,适用于火车计划的使用
//代表一个具有时间表、起始车站,终点车站,经停车站、由一些车厢资源组合形成的火车运行计划项;
//可以选择的共性方法有add()添加该项计划allocate()为该项计划分配资源run()启动该项计划cancel()取消该项计划end()
//结束该项计划getTimeslots()获取起止时间setTimeslots(List timeslots)设置起止时间
//火车计划中特有的方法有block()将正在运行的火车暂时停止recover()将暂时停止的火车恢复运行setMultiResource(List resource)为该火车计划分配多个车厢资源
// getResource()获取火车所需要的资源 setLocations(List locsAdd)为该火车计划设计一系列站点
//setTimeslots(List timeslots)设置多段运行时的时间对
//trainEntry所具有的属性:locs代表经过的地点 timeslots代表每个时间段 resources代表所用到的资源

下面对课程和飞机计划进行介绍,课程和飞机计划都不可阻塞,同时都是只使用单个的资源,所以在这两个共性的接口上再分别的继承完成任务

设计泛型资源类分别适应于飞机和课程计划
针对飞机资源的情况完善经过两个地点类和不可更改,其中不可更改作为标志接口

经过两个两个地点的设计

//代表一个具有时间表、有起飞和降落机场、由一架飞机执飞的航班;
//可以选择的共性方法有add()添加该项计划allocate()为该项计划分配资源run()启动该项计划cancel()取消该项计划end()
//结束该项计划getTimeslots()获取起止时间
//将暂时停止的火车恢复运行setResource(List resource)为该计划分配飞机资源
//getResource()获取计划所需要的资源 setLocations(loc1,loc2)为该飞机计划设计起始点和终点
//setTimeslot(Timeslot timeslot)设置起止时间对,代表飞机的起飞与到达时间
//trainEntry所具有的属性:location代表经过的地点 timeslot代表时间段 resource代表所用到的资源

针对课程类的设计考虑主要继承可更改和一个位置的接口
只使用单个资源的接口设计

设计地点属性,并设计modify方法改变计划所需要的地点

//代表一次课,具有明确的日期/时间、地点、教师。
//代表一个具有时间表、上课地点、由一位老师上课的航班;
//可以选择的共性方法有add()添加该项计划allocate()为该项计划分配资源run()启动该项计划cancel()取消该项计划end()
//结束该项计划getTimeslots()获取起止时间
//setResource(resource)为该计划分配教师资源
//getResource()获取计划所需要的资源 setLocation(loc)为该课程计划设置上课地点
//setTimeslot(Times times)设置起止时间对,代表课程的上课时间和下课时间
//CourseEntry所具有的属性:location代表上课的地点 timeslot代表时间段 resource代表上课的教师

2.3 面向复用的设计:R
航班应用中的资源是“飞机”,属性包括飞机编号、机型号(A350、B787、C919 等)、座位数、机龄(例如 2.5 年)。
高铁应用中的资源是“车厢”,属性包括车厢唯一编号、类型(商务、一等、二等、软卧、硬卧、硬座、行李车、餐车)、定员数(例如 100 人)、出厂年份。
课表应用中的资源为“教师”,属性包括身份证号、姓名、性别、职称。
代表一个泛型,在该lab中R代表一个计划项目所需要的资源种类,在选取的计划中进行选择,可以代表课程计划,火车计划或航班计划,后期通过具体的继承的时候进行类型擦除实现泛型的继承,课程通过教师资源实现,火车计划通过车厢资源实现,飞机计划通过飞机来实现。

2.4 面向复用的设计:Location

设计一个通用的地点类,分别有经度纬度地点名称和能否共享四个属性
分别构建三个继承类分别添加对应的list添加相应的资源项表示地点所具有的计划项,对边对应飞计计划,火车计划,和课程计划的地点类

2.5 面向复用的设计:Timeslot

/*
单独的“起止时间对”设计 Timeslot 类,它是一个带有起始时间和结束时间的
ADT,应包含日期(年/月/日)和时间(时/分),符合 yyyy-MM-dd HH:mm 的语
法规则。例如:
(2020-03-01 12:00, 2020-03-01 14:00)
*/
适用于所有的计划的时间的表达,在火车中表示整个列车的起止时间,飞机和课程也类似相同。
/*AF:
表示一个起止时间对
RI:
yyyy范围0001-9999 MM范围01-12 dd范围01-31 HH范围0-24 mm范围0-60
时间不能为空
safe from exposure:
final private
immutable String
*/
2.6 面向复用的设计:EntryState及State设计模式
对于不同的状态类的设计,该类主要对除了阻塞态的其他状态进行设计考虑

对每一个方法状态方法下进行设计,和打印输出信息,

相关的状态的上下文的设计
由于其中阻塞和恢复两种方法不是所有类所共有的所以在后面设计的针对于火车的类中进行具体的实现。
设计计划的名称属性,和一个timeslot用于记录计划的开始和截止时间,所有的计划都可以共用的属性

2.7 面向应用的设计:Board
Board类因功能差异较大,分别实现了三个针对飞机火车和课程的实现类flightBoard,trainBoard,courseBoard三个ADT,创建一个data类专门容纳表格中的数据类型data

Data的数据类型应用于三类中进行代理表达,在排序时候调用时间比较函数进行时间的排序对数据尽心整理

同时对是否是始发站和终点站的情况进行考量,如果是的话只有出发的data或到达的data,否则既有出发又有到达(火车的途径站点)
最后进行排序输出

2.8 Board的可视化:外部API的复用
分别在三个Board类中实现相应的可视化的API, 主要针对Jtable的一些创建方法进行学习

2.9 可复用API设计及Façade设计模式
2.9.1 检测一组计划项之间是否存在位置独占冲突
基于facade设计模式,主要实现三类功能适用于各种计划
三类功能主要有:检测各项计划之间是否存在地点独占(主要在课程计划安排中);各项计划之间是否存在资源重叠问题一个资源在同时间被多项计划使用(在各项计划中都会存在)
火车车厢重叠使用,教师课程重叠,飞机使用重叠;提取特定资源的前序计划项,返回车厢的前一个使用计划
通过构建facade模式,是各类计划直接调用该模式检查方法,完成上述的三类方法的检查,针对不同的计划方式给出不同的方法组合
课程适合于方法一二三,火车适合于方法二三,飞机适合于方法二三

针对三类计划的façade设计,分别在客户端直接调用façade接口调用进行资源冲突的情况进行输出
2.9.2 检测一组计划项之间是否存在资源独占冲突
//方法一用于检测地点独占问题,火车飞机场不适用于该方法,只有课程需要,所以这里直接采用CourseEntry的list来创建
public boolean checkLocationConflict(List entries) throws ParseException {
//delegation创建比较器,计算时间差
//首先比较两个不同的计划的地点是否相同,然后如果计划的地点相同,比较其中的时间
//返回true代表
通过对计划项的循环遍历查找是否有资源冲突的问题
2.9.3 提取面向特定资源的前序计划项

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

设计planfactory类通过委托调用getplan方法创建plan的种类和方案的名字
2.10.2 Iterator
在修改list中的元素过程中多次使用该模式:比如打印list中的资源项

在比较器中的使用

2.10.3 Strategy

2.11 应用设计与开发
2.11.1 航班应用
由于火车计划的实现相对比较复杂所以主要介绍火车计划类的实现,航班具体的实现和火车类似但更佳的简单,所需要的结构更少。所以对于火车的情况进行简要介绍。

  • 该类为飞机计划的客户端类,与客户进行交互完成飞机计划的管理任务

  • 主要需要实现的交互功能有

  • 用户提供必要信息,管理(增加、删除)可用的资源;(管理所有的航班飞机资源)

  • 用户提供必要信息,管理(增加、删除)可用的位置;(管理所有的可以共享的飞机机场站点)

  • 用户提供必要信息,增加一条新的计划项;(新增一个飞机计划;使用工厂方法模式)

  • 用户提供必要信息,取消某个计划项;(取消已经创建的但还未开始运行过的计划)

  • 用户提供必要信息,为某个计划项分配资源;(调用计划内部的setResource函数对于一个航班计划进行飞机资源的分配)

  • 用户提供必要信息,启动某个计划项;(改变飞机运行状态为运行态调用run方法)

  • 用户提供必要信息,以变更某个已存在的计划项的位置;(飞机计划中无法使用)

  • 用户提供必要信息,结束某个计划项;(飞机到达终点站,或者调用end方法)

  • 用户选定一个计划项,查看它的当前状态;(查看飞机计划现在的状态调用getstate方法)

  • 检测当前的计划项集合中可能存在的位置和资源独占冲突(利用上面完成的接口进行实现)

  • 针对用户选定的某个资源,列出使用该资源的所有计划项(利用迭代器项目利用的资源进行查找并结果输出)

  • 选定特定位置,可视化展示当前时刻该位置的信息板(利用迭代器项目占据的地点进行查找并结果输出)

    static List trainResources = new ArrayList();
    static List trainStations = new ArrayList<>();
    static List trainPlans = new ArrayList<>();

2.11.2 高铁应用

static List<TrainResource> trainResources = new ArrayList<TrainResource>();
static List<Location> trainStations = new ArrayList<>();
static List<TrainEntry> trainPlans = new ArrayList<>();

分别创建三个list:trainResources用于容纳所有的火车车厢资源,trainstation用于容纳所有的站点资源,trainplans用于容纳所有的资源项。
需要实现的功能:
System.out.println(“please input the command:”);
System.out.println(“1.管理所有的火车车厢资源”);
System.out.println(“2.管理所有的可以共享的火车车站站点”);
System.out.println(“3.新增一个火车计划”);
System.out.println(“4.取消已经创建的但还未开始运行过的计划”);
System.out.println(“5.对计划向进行火车车厢资源的分配”);
System.out.println(“6.启动某个火车的计划项”);
System.out.println(“7.阻塞某个火车计划的运行状态”);
System.out.println(“8.恢复运行某个正在阻塞状态的火车计划”);
System.out.println(“9.结束正在运行的火车计划项”);
System.out.println(“10.查看火车计划项的目前状态”);
System.out.println(“11.检测当前的计划项集合中可能存在的位置和资源独占冲突”);
System.out.println(“12.列出使用该资源的所有计划项”);
System.out.println(“13.展示当前时刻该位置的信息板”);
System.out.println(“14.结束火车管理应用”);
1.输入添加的资源

2输入添加的地点
方法情况与一类似创建火车资源和火车计划站点的命令行结果

3新增计划:需要增加一个实例,同时给火车计划安排时间和经过的站点,通过按顺序的增加站点的名称,分别选取站点的顺序,分别添加相应的出发和到达的时间情况。

4.取消一个计划:主要对commonplan类的方法的调用,调用相应的状态的计划进行打印,然后调整计划的状态

  1. 对计划向进行火车车厢资源的分配
    设置火车的状态为已分配资源状态
    火车需要的资源为火车车厢,先选择计划的名称,然后需要在现有的火车车厢资源中选取给改车次计划,并设置状态

6789设置相应的状态和4类似不再赘述
查看火车计划项的目前状态与前面查询某状态下的计划类似,直接调用迭代器模式进行查询并输出结果。

11调用前面完成的façade模式的接口直接调用完成查询矛盾计划的任务

12 13
直接调用相应的类进行委托实现
2.11.3 课表应用
相比火车计划基本类似,并更加简单,所以这里不在过多赘述
/*

  • 该类为课程计划的客户端类,与客户进行交互完成课程计划的管理任务

  • 主要需要实现的交互功能有

  • 用户提供必要信息,管理(增加、删除)可用的资源;(管理所有的课程教师资源)

  • 用户提供必要信息,管理(增加、删除)可用的位置;(管理所有的不可共享的课程教室地点)

  • 用户提供必要信息,增加一条新的计划项;(新增一个课程计划;使用工厂方法模式)

  • 用户提供必要信息,取消某个计划项;(取消已经创建的但还未开始运行过的计划)

  • 用户提供必要信息,为某个计划项分配资源;(调用计划内部的setResource函数对于一个课程计划进行课程资源的分配)

  • 用户提供必要信息,启动某个计划项;(改变课程运行状态为运行态调用run方法)

  • 用户提供必要信息,以变更某个已存在的计划项的位置;(课程计划存在,可以在开始状态之前改变某个上课的教室位置)

  • 用户提供必要信息,结束某个计划项;(课程结束,或者调用end方法)

  • 用户选定一个计划项,查看它的当前状态;(查看课程计划现在的状态调用getstate方法)

  • 检测当前的计划项集合中可能存在的位置和资源独占冲突(利用上面完成的接口进行实现)

  • 针对用户选定的某个资源,列出使用该资源的所有计划项(利用迭代器项目利用的资源进行查找并结果输出)

  • 选定特定位置,可视化展示当前时刻该位置的信息板(利用迭代器项目占据的地点进行查找并结果输出)

     //定义TeachingResources容纳所有的可以用的课程资源
     //定义CourseStations容纳所有可以共享的课程机教室
     //定义CoursePlans容纳所有的CourseEntry课程计划项
     
     static List<TeachingResource> teachingResources = new ArrayList<TeachingResource>();
     static List<Location> courseStations = new ArrayList<>();
     static List<CourseEntry> coursePlans = new ArrayList<>();
    

2.12 基于语法的数据读入
修改“航班”应用以扩展该功能。
主要完成正则表达式的学习,并进行一定的应用
构建一个数组和一个循环的结构,把每一个具体的飞机的计划项出去出来进行分析,同时对数据进行正则表达式的判断,如果数据错误进行相应的输出和判断错误,对给出的数据进行检测,均未发现有错误的数据。
部分判断的实例

2.13 应对面临的新变化
只考虑你选定的三个应用的变化即可。
2.13.1 变化1:航班支持经停,即包含最多 1 个中间经停机场,例如哈尔滨-威海-深圳。
评估之前的设计是否可应对变化、代价如何
如何修改设计以应对变化
原先飞机采用只可以停两个站点的接口进行实现,现在改用和火车一样的接口进行重构实现一个新的类,用飞机继承该类,同时相应的客户端需要更改关于计划设置时候的需要进行的输出提示,同时相关的打印信息也需要更改。
先后更改具体的逐步继承来的实现类的名字和里面的具体方法,主要就是继承的从两个地点改为多个地点然后进行实现新添加的方法,同时还需要将原先的unblockable改为blockable进行实现。

在飞机计划的类中需要更改3添加计划和11打印当前的计划,不过由于火车计划中已经实现了相应的方法,适用于经过多个车站的情况,所以可以直接复用,实现方法的更改

Board中对于显示的要求有所更改,但可以直接复用train的代码,应用于飞机计划的设计情况。
2.13.2 变化2如果高铁车次已经分配了车厢资源,则不能被取消。
评估之前的设计是否可应对变化、代价如何
如何修改设计以应对变化
这个修改相对简单,在火车计划实现类中对于状态的属性修改进行相应的限定修改,修改可以取消的前置条件情况。取消和状态相关,之前采用状态设计模式,所以在状态设计模式中对状态的变化进行更改,在分配状态下无法改为取消状态。

2.13.3 变化3:课程可以有多个教师一起上课,且需要区分次序(即多名教师的优先级)。
评估之前的设计是否可应对变化、代价如何
如何修改设计以应对变化
与变化一类似,需要进行前置类的更改,将原有的单个资源替换为可以使用多个资源的类,然后再通过计划项进行继承,同时课程的计划类相应的提示和表格输出需要进行相应的修改。
将原有的singleresource接口改为multiresource接口重新进行实现和相关的方法进行替换,由于火车类中已经实现过multiresource可以直接复用相关代码

对于教师的查询改为采用双重循环的结构进行查找

2.14 Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚314change分支和master分支所指向的位置。
3 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 时间段 计划任务 实际完成情况
5.1-5.18 整天 Lab3 完成

4 实验过程中遇到的困难与解决途径
遇到的难点 解决途径

正则表达式不熟悉
查找学习

对于可复用的设计模式
查找学习

5 实验过程中收获的经验、教训、感想
5.1 实验过程中收获的经验和教训
5.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在五个不同的应用场景下使用,你是否体会到复用的好处?
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
(4) 在编程中使用设计模式,增加了很多类,但在复用和可维护性方面带来了收益。你如何看待设计模式?
(5) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
(6) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过五周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
(7) “抽象”是计算机科学的核心概念之一,也是ADT和OOP的精髓所在。本实验的五个应用既不能完全抽象为同一个ADT,也不是完全个性化,如何利用“接口、抽象类、类”三层体系以及接口的组合、类的继承、设计模式等技术完成最大程度的抽象和复用,你有什么经验教训?
(8) 关于本实验的工作量、难度、deadline。
(9) 到目前为止你对《软件构造》课程的评价。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值