软件构造 2019

某公司拟设计和开发一个停车场管理系统,其基本需求陈述如下:
(1) 一个停车场有 n 个车位(n>=5),不同停车场包含的车位数目不同。
(2) 一辆车进入停车场,如果该停车场有空车位且其宽度足以容纳车的宽度,则可以在此停车。
(3) 停在停车场里的车,随时可以驶离停车场,根据车型或时间自动计费。
(4) 停车场管理员可以随时查看停车场的当前占用情况。
下图给出了一个包含 13 个停车位的小型停车场示例图,其中 1-8 号停车位较窄,9-13 号停车位较宽。
在当前状态下,第 1、3、7、9、10 号车位被占用,其他车位空闲。
在这里插入图片描述
2(10分)请为ParkingField接口增加一一个静态工厂方法,构造并返回一个ConcreteParkingField对
象。请给出静态工厂方法的spec和Java代码。由于ConcreteParkingField类代码中尚未给出其构
造函数,你需要根据其rep设计和实现其合理的构造函数,以便将其用于你的静态工厂方法中。
ConcreteParkingField
spec:

/**
静态工厂方法用于创建一个停车场的实例
@ param name 停车场的名称 不能为空,长度大于 0
@ param lotsNumer 停车场的车位数 要求lotsNumer>=0
@ param lots 停车场中车位信息 要求数目和车位数相等
@ return 返回成功创建的停车场的实例

Java实现:

ParkingFieldFactory:

public static ParkingField ParkingFieldFactory(String name ,int lotsNumber,Map<Integer,Integer> lots){
	return new ConcreteParkingField(name,lotsNumber,lots);
}

ConcreteParkingField构造方法:

public static ConcreteParkingField(String name ,int lotsNumber,Map<Integer,Integer> lots){
	 this.name=name ;
	 this.lotsNumber=lotsNumber;
	 for(Integer x:lots.keySet()){
	 	this.lot.add(new Lot (x,lots.get(x)));
	 }
}

请写出 ConcreteParkingField 类的 RI 和 和 checkRep()
RI:
name 不能为空,长度大于 0
lotsNumber 最少 5 个车位
lots 不同停车位对象的编号不能相同 ,且数目等于lotsNumber
status Key 和 Value 的值均不能为 null

private void checkRep() {
	assert !name.isEmpty();
	assert lotsNumber>=5;
	assert lots.size==lotsNumber;
	Set s=new HashSet<>();
	for (lots l:Lot){
		s.add(l.getNumber);
	}
	assert s.size==lot.szie;
	for(Car c:status.values()){
		assert c!=null;
	}
}

4-1 (10 分) 有些停车场是无人管理的公共停车场,有些则由专门公司管理。上述 ConcreteParkingField
的 rep 不支持后者。
请使用 decorator 设计模式,在不改变 ConcreteParkingField 的情况下,同时做到:
(a) 在创建 ParkingField 对象时包含“公司”信息 (String company)
(b) 车辆在此类停车场进行停车( 调用 parking 方法) 和驶离( 调用 departure 方法) 时,能打印欢迎和告别信息。

(1) 简要阐述如何修改和扩展现有设计以实现该目标 (7 分);
(2) 在 client 代码中,给定一个 ParkingField 对象,如何创建一个由“HIT”管理的停车场对象(3 分)。

ParkingField pf = ...; //此处无需补充,假设 pf 已成功创建
ParkingField pfWithCompany = ...; //用你的 decorator 方案,如何实现?

(1) 使用decorator模式,讲ParkingField视作基础类,对其进行装饰
UML图:
在这里插入图片描述
在抽象ParkingFieldDecorator类中对几个基础方法进行delegate到ConcreteParkingField类中已实现的方法中,
companyParkingField

public companyParkingField(String companyname ,ParkingFiled pf){
	this.companyname=companyname ;
	super(pf);
}
@override
parking(Car c,int num)....{
	super()....;
	System.out.println(.....).....;
}
#其余方法的改造类似

(2)客户端代码:

ParkingField companyPF=new companyParkingField("HIT",new ConcreteParkingField);

创建
4-2 (10 分) 考虑将来对 ParkingField 的功能扩展,使用 visitor 模式改造当前设计。例如要扩展的一
个功能是统计停车场当前时刻占用比例(= 已停车的车位数量/总车位数) 。
(1) 简要阐述如何修改和扩展现有设计以实现该目标 (7 分);
(2) 在客户端代码中,给定一个 ParkingField 对象,如何使用 visitor 统计当前时刻占用比例 (3 分)。

ParkingField pf = ...; //此处无需补充,假设 pf 已成功创建
//并且进行了一系列 parking 和 departure 操作
double fullRatio = ...; //用你的 visitor 方案,如何实现?

(1)
首先在ParkingField 类中增添一个accept()方法,并在ConcreteParkingField类中继承并实现它
创建visitor接口ParkingFieldVisitor,其中只有visit方法

double accept(ParkingFieldVisitor  pfv){
	pfv.visit(this);
}

并实现visitor接口得到ConcreteVisitor

public double visit(ParkingField PF){
.....
实现业务逻辑
}

UML类图
在这里插入图片描述
客户端调用:

ParkingField pf = ...; //此处无需补充,假设 pf 已成功创建
//并且进行了一系列 parking 和 departure 操作
double fullRatio = pf.accept(new ConcreteVisitor());

**5.1:**停车场管理系统启动时,主程序读入外部文本文件已创建一系列 ParkingField 对象。该文本文
件遵循特定的语法格式,每个以 PF 开头的行代表一个 ParkingField 对象,语法说明如下所示。请写出:
(1) PF 的产生式形式的语法定义 (5 分) ;(2) PF 的正则表达式 (5 分) 。

(1) PF::= 一个由<>括起来的字符串,分为三部分,分别代表停车场名字、最大车位数、公司名字,
三部分之间由逗号“,”分割。
(2) 停车场名字 ::= 字符串,长度不限。可以由一个单词或多个单词构成,单词由字母或数字构成,
单词之间只能用一个空格分开。
(3) 停车场最大车位数 ::= 自然数,其值最小为 5。不能为 012、0012 的形式,只能为 12 的形式。
(4) 公司名字 ::= 与停车场名字的语法规则一致,但可以为空。若该部分为空,表示该停车场没有公
司管理 (即公共停车场)。
以下是三个例子:
PF::=<92 West Dazhi St,120,HIT>
PF::=<Expo820Roadside,10,> //无公司管理的停车场,最后一个逗号之后为空
PF::=<73 Yellow River Rd,50,Harbin Institute of Technology>

1)产生式
PF::=<Name,lotsNumber,Company>
Name::= (word\s)+
lotsNumber::=[5-9]|[1-9][1-9]+
Company::=(word\s)*
word::=[0-9a-zA-Z]+

2)正则
< ([ 0-9a-zA-Z]+\s) + [5-9]|([1-9][1-9]+) ( [ 0-9a-zA-Z]+\s) )* >


5.2: ParkingField 需要具备遍历其中所停的所有 Car 对象的能力。拟使用以下形式的 client 端代
码,按车辆所在停车位编号由小到大的次序,逐个读取所停车辆。请扩展现有设计方案( 修改/ 扩展哪些
ADT) ,在下方给出你的设计思路描述,必要时可给出关键代码示例或 UML 类图辅助说明

使用迭代器模式进行设计

companyParkingField要实现Iterable接口
然后实现iterator()方法:返回一个迭代器

public Iterator<Car>(){
	return new  ParkingFieldIterator();
}

并实现内部类:

private class ParkingFieldIterator implements Iterator{
	private int cur=-1private int size=status.size();
	public boolean hasNext(){
		return cur<size;
	}
	public Car next(){
	  	#没写具体实现逻辑
		return .....status.get(cur);
		
	}
	
}


6 (10 分) ParkingField 接口中的 void parking (Car c, int num) 方法的 spec 中给出了四个异常,
如下所示,每个异常后面的文字表示当异常 e 抛出的时候用 e.getMessage() 所能输出的信息:
LotOccupiedException 编号为 num 的车位已被其他车辆所占用
LotTooNarrowException 编号为 num 的车位宽度小于 c 的宽度,c 无法在此停车
NoSuchLotException 该停车场不存在编号为 num 的车位
CarAlreadyParkingException 车辆 c 已停在该停车场且没有驶离,不能再次停车。
假设上述四个异常均已经实现为 checked 异常,且均具有一个构造方法,其参数如下:
LotOccupiedException 和 NoSuchLotException 参数均为 int num
LotTooNarrowException 参数为(Car c, int num)
CarAlreadyParkingException 参数为 Car c
请利用之前的 ADT 代码与 spec 说明,写出该方法的 Java 实现代码。答题区域很大,无需全部填满。

public void parking(Car c, int num) throws LotOccupiedException, NoSuchLotException,
LotTooNarrowException, CarAlreadyParkingException
{
	for(Lot l:status.getKeyset()){
		if(l.getNumber==num)
			throws new LotOccupiedException(num);
	}
	for(Car car:status.values()){
		if(car.equals(c)){
		throws new CarAlreadyParkingException(c);
		}
	}
	for(Lot l:lots){
		if(l.getNumber==num){
			if(lot.wideEnough(c)){
				return 
			}
			throws LotTooNarrowException (c,num)
		}
	}
	throws NoSuchLotException ();
}

7.为司机开发了一个客户端,允许司机寻找车位进行停车。为每个车辆设定一个线程,代码如下:

public class ParkingThread implements Thread {
	private final ParkingField pf;
	//司 机要进入的停车场
	private final Car car;
	//司机驾驶的车辆
	public ParkingThread(ParkingField pf, Car car) {
		this.pf = pf;
		this.car = car;
	}
	@Override
	public void run( ) {
		int freeLot = -1;
		while (freeLot == -1) {
			try {
				freeLot = pf . getOneFreeLot(car);
			}
			catch (ParkingFieldFullException e) {
				Thread. sleep(1000);
				continue ;
			}
		}
		pf . parking(car, freeLot);
		)
	}
	}

检查上述 19 行代码,找出无法通过 static type checking 之处,并指出如何修改正确。
(1) public class ParkingThread implements Thread 线程类并不是一个抽象类或者接口,因此不能implements,可以改为extends Thread或者是 implements Runable
(2) 对于pf . parking(car, freeLot); ,因为这个方法产生的异常均已经实现为 checked 异常,所以必须对其进行处理
,可以用throw 或者try-catch

假设你已完成(1)的修改,之后在主程序中启动了多个上述线程,代码如下。为保证 thread
safe,请指出如何修改 ConcreteParkingField、Lot、Car、ParkingThread 的代码, 以消除线程
不安全风险,并能尽可能保证程序的并发执行效率。修改代码时不应改变程序已有逻辑。

ParkingField pf = ...; //假设 pf 已成功创建
List<Car> cars = ...; //假设 cars 已成功创建
for(Car car : cars) {
Thread t = new Thread(new ParkingThread(pf, car));
t.start();
}

ConcreteParkingField:对集合lots和Map用Collections.synchronizedSet和…Map处理,对类中方法加上synchronized关键字,保证类的每一个方法都是原子的
Lot:rep用final修饰
Car:Car类是线程安全的,无需修改
ParkingThread :

synchronized(pf){
#对每台车寻找停车场的方法进行加锁,保证每台车寻找时停车场不会在其他线程中发生改变导致出现错误
while (freeLot == -1) {
			try {
				freeLot = pf . getOneFreeLot(car);
			}
			catch (ParkingFieldFullException e) {
				Thread. wait();
				#如果车辆一直找不到停车位,不应该sleep死循环,而是使用wait()挂起
				continue ;
			}
		}
		pf . parking(car, freeLot);
		notifyAll();
		#当一台车找到停车位之后,唤醒线程池中其他线程
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值