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

本实验报告详细介绍了面向可复用性和可维护性的软件构造技术,涉及航班、高铁和学习活动管理应用场景。报告涵盖了PlanningEntry的共性操作、局部特性和个性化设计方案,包括位置、资源、状态和API设计。同时,报告还探讨了设计模式的应用,如Factory Method、Iterator和Strategy,并展示了应对新变化的灵活性。
摘要由CSDN通过智能技术生成

Github地址

1 实验目标概述

本次实验覆盖课程第 3、4、5 章的内容,目标是编写具有可复用性和可维护

性的软件,主要使用以下软件构造技术:

  • 子类型、泛型、多态、重写、重载

  • 继承、代理、组合

  • 常见的 OO 设计模式

  • 语法驱动的编程、正则表达式

  • 基于状态的编程

  • API 设计、API 复用

2 实验环境配置

简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。

特别是要记录配置过程中遇到的问题和困难,以及如何解决的。

3 实验过程

3.1 待开发的三个应用场景

应用场景:航班管理、高铁车次管理、学习日程管理

共性:

都有创建一个新计划项的功能;

都可以为对应的计划项申请资源。

都可以启动、取消、完成指定的计划项;

每个计划项都有状态,应用可以获得这个状态;

获得每个计划项的名字。

差异:

航班计划项的位置数量只有两个,包括起止点,高铁计划项的有一个起点、一组中间位置和一个终点,而学习活动只有一个位置;

这三个计划项的位置都可以提前设定,但航班计划项和高铁计划项的位置设定后不可更改,而学习活动的位置在设定后可以更改;

航班计划项的飞机资源是单个可区分的,高铁计划项的资源是多个有序排列的一组可区分的车厢资源,学习活动的资源是多个不带次序的不可区分的材料资源;

航班计划项和学习活动计划项的时间表是一个起止时间对,并且是不可阻塞的,而高铁计划项的时间表是一组起止时间对,并且可以阻塞,这三个计划项的时间在创建的时候都可以设定。

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

3.2.1 PlanningEntry<R>的共性操作

  1. 实现PlanningEntry<R>
方法 方法作用
public static <R> PlanningEntry<R> flightEntry (String name, Location departure, Location arrival, String start, String end) 静态工厂方法,创建一个航班计划项
public static<R> PlanningEntry<R> trainEntry (String name, List<Location> locationList, List<String> times) 静态工厂方法,创建一个高铁计划项
public static <R> PlanningEntry<R> activityEntry (String name, String start, String end) 静态工厂方法,创建一个活动计划项
public boolean allocate() 将计划项状态设置为已分配状态
public boolean running() 将计划项状态设置为运行状态
public boolean cancel() 将计划项状态设置为已取消状态
Public boolean End() 将计划项状态设置为完成状态
public String getName() 获取计划项的名字
public String getState() 获得计划项状态
  1. 实现CommonPlanningEntry<R>

(1)数据域:保存计划项状态的state和表示计划项名字的name。

protected State state;
private final String name;

(2)方法:

① 构造方法:把计划项的状态设为未分配(WAITING),计划项命名为name。

/**
 * 创建一个通用计划项
 * 
 * @param name 计划项的名字,非空
 */
public CommonPlanningEntry(String name) {
   
	state = new Waiting();
	this.name = name;
}

② allocate()、running()、cancel()、end()方法的实现都是调用state.nextState()方法,然后判断返回的状态是否是目的状态,如果是,返回true,否则说明不能转移到该状态,返回false。实现样例如下:

@Override
public boolean allocate() {
   
	state = state.nextState("ALLOCATED");
	if (state.state().equals("ALLOCATED"))
		return true;
	else
		return false;
}

③ getState()方法调用state.state()方法,获得状态。

  1. 实现计划项集合类PlanningEntryCollection<R>
private final List<PlanningEntry<R>> entries = new ArrayList<>();

(1)数据域:计划项列表

(2)方法:

方法 实现
public boolean addEntries(PlanningEntry<R> entry) 判断计划项是否为空,若为空返回false,不为空则加入到列表返回true
public List<PlanningEntry<R>> getEntries() 用Collections.unmodifiableList()转换成不可修改的列表
public Iterator<PlanningEntry<R>> iterator() 返回entries.iterator()
public void sort() 调用entries.sort()对计划项排序

(3)AF、RI、Safety from rep exposure:

Abstraction function:

AF(entries)=现实中的计划项清单

Representation invariant:

entries!=null

Safety from rep exposure:

所有的数据域都是私有的用final限定

获得entries时使用Collections.unmodifiableList()转化为不可更改的list返回

  1. 测试:

(1)测试静态方法:

Test strategy

测试flightEntry()

通过getName()、getState()、getDeparture()、getArrival()、getTimeslot()观察

测试trainEntry()

通过getName()、getState()、getLocations()、getTimeslot()观察

测试activityEntry()

通过getName()、getState()、getLocation()、getTimeslot()观察
在这里插入图片描述

(2)测试实例方法:

Test strategy

测试allocate()

此时的状态为WATIING或ALLOCATED,此时的状态不为WAITING和ALLOCATED

测试running()

此时状态为ALLOCATED或BLOCKED或RUNNING,此时状态不为ALLOCATED和BLOCKED和RUNNING

测试cancel()

此时状态为WATIING或ALLOCATED或BLOCKED或CANCELLED,此时状态不为WATIING和ALLOCATED和BLOCKED或CANCELLED

测试end()

此时状态为RUNNING或ENDED,此时状态不为RUNNING和ENDED

测试getName()

测试是否返回计划项的名称

测试getState()

测试是否正确返回计划项的状态

(3)测试PlanningEntryCollection

Test strategy

测试AddEntries()

添加的计划项未null,计划项不为null

测试getEntries()

当没有计划项时,当有计划项时

测试sort()

测试能够按照时间的先后顺序给计划项排序
在这里插入图片描述

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

3.2.2.1 实现可阻塞功能

定义具有阻塞功能的接口BlockableEntry,当计划项是可以被阻塞的时候实现此接口,接口声明方法如下:

/**
 * 把计划项的状态设置为阻塞
 * 
 * @return false 设置失败 true 设置成功
 */
public boolean block();
3.2.2.2 实现两个位置
  1. 定义表示出发和抵达两个地点的接口TwoLocationEntry,对与选择的航班计划项可以实现此接口,方法声明如下:
/**
 * 获得出发地点
 * 
 * @return 出发地点
 */
public Location getDeparture();

/**
 * 获得目的地点
 * 
 * @return 目的地点
 */
public Location getArrival();
  1. 定义一个immutable的实现该接口的类TwoLocation。

(1)数据域:表示开始位置和结束位置。

 private final Location departure, arrival;

(2)方法:

方法 作用
public TwoLocation() 构造方法
public Location getDeparture() 获得出发地点
public Location getArrival() 获得到达地点
public String toString() 把存储的位置按照“departure->arrival”的形式输出
public boolean equals() 判断两个对象是否相同

(3)AF、RI、Safety from rep exposure:

Abstraction function:

AF(departure,arrival)=现实中从departure位置到arrival位置

Representation invariant:

departure!=null

arrival!=null

Safety from rep exposure:

所有的数据域都是私有的且使用final限定

  1. 测试:

(1)接口方法:

Test strategy

测试getDeparture

测试返回的位置是否和期望相同

测试getArrival

测试返回的位置是否和期望相同

(2)子类型方法:

Test strategy

测试toString()

测试返回的字符串是否与期望的相同

测试equals()

相同的两个位置,两个位置不相同
在这里插入图片描述

3.2.2.3 实现多个有序的位置
  1. 定义一个表示多个不可更改的位置的接口MultipleLocationEntry,对于选择的高铁计划项可以实现此接口,方法声明如下:
/**
 * 获得途径的所有地点
 * 
 * @return 途经地点的list
 */
public List<Location> getLocations();
  1. 定义一个immutable的实现该接口的类MultipleLocation。

(1)数据域:表示一组有序的位置

private final List<Location> locations;

(2)方法:只有一个构造方法和一个实现的接口定义的方法。

public MultipleLocation(List<Location> locations) {
   
	this.locations = locations;
}
@Override
public List<Location> getLocations() {
   
	return Collections.unmodifiableList(locations);
}

(3)AF、RI、Safety from rep exposure:

Abstraction function:

AF(locations)=以locations中的对象为位置且按顺序排列的一组位置

Representation invariant:

locations!=null

Safety from rep exposure:

所有的数据域都是私有的且使用final限定

返回位置列表时使用Collections.unmodifiableList()返回一个不可修改的列表

  1. 测试:

Test strategy

测试getLocations()

测试返回的位置列表与期望是否相等
在这里插入图片描述

3.2.2.4 实现可修改的单个位置
  1. 定义一个表示可变更的单个位置的接口ModifiableSingleLocationEntry,对于选择的高铁计划项可实现此接口,方法声明如下:
 /**
 * 设置位置
 * 
 * @param location 待设置的位置
 * @return false 设置失败,位置为空 true 设置成功
 */
public boolean setLocation(Location location);

/**
 * @return 获得位置
 */
public Location getLocation();
  1. 定义一个mutable的实现该接口的类ModifiableSingleLocation。

(1)数据域:表示一个位置。

private Location location;

(2)方法:

实现接口中的方法,public boolean setLocation(Location location):当location是null是返回false,否则把当前的位置改为location返回true。

(3)AF、RI、Safety from rep exposure:

Abstraction function:

AF(location)=一个现实中的位置

Representation invariant:

location!=null

Safety from rep exposure:

所有的数据域都是私有的

  1. 测试:

Test strategy

测试setLocation()

位置为空,位置不为空

测试getLocation()

测试返回的位置是否和预期相同
在这里插入图片描述

3.2.2.5 实现一组有序资源
  1. 定义表示一个可复用的有序的资源组的接口MultipleSortedResourceEntry<R>,对于选择的高铁计划项可实现此接口。方法声明如下:
/**
 * 获得资源组
 * 
 * @return 一个资源组的列表
 */
public List<R> getResources();
  1. 定义一个immutable的实现该接口的类MultipleSortedResource<R>。

(1)数据域:表示一组有序的资源。

private final List<R> train;

(2)方法:

/**
 * 创建一个资源组
 * 
 * @param train 指定的资源组,非空,元素个数大于0
 */
public MultipleSortedResource(List<R> train) {
   
	this.train = train;
}

@Override
public List<R> getResources() {
   
	return Collections.unmodifiableList(train);
}

(3)AF、RI、Safety from rep exposure:

Abstraction function:

以train中的顺序排好序的,有train.size()个个体的一组资源

Representation invariant:

train!=null

train.size()>0

Safety from rep exposure:

所有的数据域都是私有的且使用final限定

获得资源时用Collections.unmodifiableList()转化为不可变的List输出

  1. 测试:

Test strategy

测试getResources()

测试返回的资源是否与期望的相等
在这里插入图片描述

3.2.2.6 实现一个起止时间对
  1. 定义一个代表可被预设的起止时刻的时刻表的接口PresetSingleTimeslotEntry,对于选择的航班计划项和学习活动计划项可实现此接口,方法声明如下:
/**
 * 获得起止时刻
 * 
 * @return 一个有起止时刻的时刻表
 */
public Timeslot getTimeslot();
  1. 定义一个immutable的实现该接口的类PresetSingleTimeslot。

(1)数据域:表示一个起止时间对。

private final Timeslot timeslot;

(2)方法:

/**
 * 创建起止时刻表
 * 
 * @param start 开始时间
 * @param end   结束时间
 */
public PresetSingleTimeslot(String start, String end) {
   
	this.timeslot = new Timeslot(start, end);
}

@Override
public Timeslot getTimeslot() {
   
	return timeslot;
}

(3)AF、RI、Safety from rep exposure:

Abstraction function:

AF(timeslot)=计划项运行过程中的起止时刻

Representation invariant:

timeslot!=null

Safety from rep exposure:

所有的数据域都是私有的且使用final限定

  1. 测试:

Test strategy

测试getTImeslot()

测试返回的时间表是否和预期的相等
在这里插入图片描述

3.2.2.7 实现一组起止时间对
  1. 定义一个表示被预设好的一组有序的时间表的接口PresetMultipleTimeslotEntry,对于高铁计划项可实现此接口,方法声明如下:
/**
 * 获得时刻表
 * 
 * @return 一个时刻表
 */
public List<Timeslot> getTimeslot();
  1. 定义一个immutable的实现该接口的类PresetMultipleTimeslot。

(1)数据域:表示一组有序时间对的列表。

private final List<Timeslot> timeslot = new ArrayList<>();

(2)方法:

/**
 * 创建一个时刻表
 * 
 * @param times 被预设的时间表,非空,times.size()>0
 */
public PresetMultipleTimeslot(List<String> times) {
   
	for (int i = 0; i < times.size(); i += 2) {
   
		this.timeslot.add(new Timeslot(times.get(i), times.get(i + 1)));
	}
}

@Override
public List<Timeslot> getTimeslot() {
   
	return Collections.unmodifiableList(timeslot);
}

(3)AF、RI、Safety from rep exposure:

Abstraction function:

以timeslot中的顺序排好序的,有timeslot.size()个个体的一组资源

Representation invariant:

timeslot!=null

timeslot.size()>0

Safety from rep exposure:

所有的数据域都是私有的且使用final限定

获得资源时用Collections.unmodifiableList()转化为不可变的List输出

  1. 测试:

Test strategy

测试getTImeslot()

测试返回的时间表是否和预期的相等
在这里插入图片描述

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

3.2.3.1 实现FlightEntry<R>

继承抽象类CommonPlanningEntry,实现接口TwoLocationEntry, PresetSingleTimeslotEntry。

  1. 数据域:表示起止位置的twoLocations,表示起止时刻表的timeslot,表示所占用的资源的plane。
private final TwoLocationEntry twoLocations;
private final PresetSingleTimeslotEntry timeslot;
private R plane;
  1. 方法:

(1)构造方法:调用父类CommonPlanningEntry的构造方法,为计划项命名,然后设定twoLocations和timeslot为给定起止位置和时刻表。

(2)public boolean allocatePlane(R plane):调用父类的allocate()方法,判断返回值,如果返回false,说明当前的状态不可为计划项分配资源,直接返回false,否则说明可以分配资源,把参数plane赋给计划项资源this.plane,然后返回true表示分配成功。

(3)public R getResource():获得资源,直接返回plane。

(4)public Location getDeparture():获得出发位置。

public Location getArrival():获得到达位置。

委托给twoLocations,分别调用twoLocations.getDeparture()和twoLocations.getArrival()获得起止位置。

(5)public Timeslot getTimeslot():委托给timeslot,调用timeslot.getTimeslot()获得起止时间对。

(6)public int compareTo(PlanningEntry<R> entry):比较两个计划项的开始时间,当this在entry之前开始,返回-1,当this在entry之后开始,返回1,两者同时开始,返回0。

  1. AF、RI、Safety from rep exposure:

Abstraction function:

AF(twoLocations, timeslot, plane)=一个timeslot.getStartTime()从

twoLocations.getDeparture()出发timeslot.getEndTime()到达twoLocations.getArrival()的航班

Representation invariant:

twoLocations!=null

timeslot!=null

Safety from rep exposure:

twoLocations,timeslot数据域都是私有的用final限定

plane是私有的

  1. 测试:

Test strategy

测试allocatePlane()

状态可分配资源,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值