题目
某公司拟设计和开发一个停车场管理系统,其基本需求陈述如下:
(1) 一个停车场有n个车位(n>=5),不同停车场包含的车位数目不同。
(2) 一辆车进入停车场,如果该停车场有空车位且其宽度足以容纳车的宽度,则可以在此停车。支持自动分配车位和指定停车位两种分配方式。
(3) 停在停车场里的车,随时可以驶离停车场。
(4) 停车场管理员可以随时查看停车场的当前占用情况。
我们能够发现,一个停车场里面肯定是包含停车位和车(讲课的时候里面还有收费这种操作,所以应该还有一个记录收费的类)。很明显的,停车位这个类应该包含停车位的编号还有停车位的宽度(宽度是一定要有的,这个是判断车是否能够停进停车位的重要指标);车这个类应该包括车牌号和车的宽度。我们发现在大多数时候一旦停车位和车确定了之后就很难再改变了,所以我们可以将这两个类变成immutable的类,这会大大简便我们能之后的操作,而且非常的安全。所以我们要把这些量都标记成private final的。
private final int number;
private final int width;
private final String plateNo;
private final int width;
之后我们就应该写一下这两个类的AF、RI和Safety from rep exposure:
//AF:
//停车位由唯一的编号和宽度值表示
// Representation invariant:
// 停车位编号>0
// 宽度>150
// Safety from rep exposure:
// 所有的表示都是private和 final的
//AF:
//车辆由唯一的车牌号和宽度值表示
// Representation invariant:
// 车牌号不为空,长度大于0
// 车辆宽度>100
// Safety from rep exposure:
// 所有的表示都是private和 final的
之后,由于这两个类都是immutable的,所以不能有mutator,所以我们就写构造方法constructor和get方法(能自动生成)就好了,然后由于比较车和停车位的时候只需要分别比较他们的宽度是否相等就好了(因为这里面大多数时候只关心宽度,我们不用比较车牌号和停车位编号),所以我们需要重写equals方法和hashCode方法(这里面编译器也能自动生成)
public Lot(int number, int width) {
this.number=number;
this.width=width;
checkRep();
}
public Car(String plateNo, int width) {
this.plateNo = plateNo;
this.width = width;
checkRep();
}
之后呢我们就来实现记录信息这个类,去过停车场停车的人都知道,最后的停车信息应该是:“某某牌号的车在某某停车位停了某某时间,消费总计某某钱”,所以我们很容易的就能知道这里面的变量应该是车,停车位,时间和钱。这里面一看我们就能看出来,这个类肯定不是immutable的,因为时间里面我们会计算到结束时间,而结束时间是时刻都在变化的,所以那个时间也应该是mutable的,所以这个类是个mutable的类。但是我们应该尽可能的加private final。
private final Car car;
private final Lot lot;
private LocalDateTime timeIn= LocalDateTime.now();
private LocalDateTime timeOut = null;
private double fee=0.0;
之后又开始写AF、RI和Safety from rep exposure:
//Abstraction function:
//代表一个停车记录record
//车辆record.getCar()在record.getTimeIn()时刻停到了record.getLot()车位上,
//在record.getTimeOut()离开,花费record.getFee()元
// Representation invariant:
// timeOut为null,或者 timeOut>=timeIn
// Safety from rep exposure:
// 所有的表示都是private的,都是不可变数据类型,通过set和get控制安全性
然后我们就应该开始写这个类的构造方法,我们应该对于这五个量写get方法(编译器自动生成),最后我们应该写一个toString方法,为了输出信息
public Record(Car car, Lot lot) {
this.car = car;
this.lot = lot;
}
然后就应该是写计算费用的方法,我们是每半小时10元,不足半小时按半小时计算,所以我们应该先以当前时间为结束时间设置结束时间然后计算费用
public double calcFee(){
if (timeOut==null) //如果未指定离场时间,则按照当前时间为准
setTimeOut();
Duration duration = Duration.between(timeIn,timeOut);
if (duration.getSeconds()<0) //如果离场时间小于入场时间,则返回-1。也可以采用抛出异常方式处理
return -1;
long seconds= duration.getSeconds();//1000/60.0;//相差的分钟数
double fee = 0.0;
if (seconds % (60*30) ==0 )
fee= 10*(seconds/(60*30));
else
fee= 10*(seconds/(60*30)+1);
this.fee=fee;
return fee;
}
最后我们就应该写停车场这个类了(老师在写之前先写一个接口,然后再实现这个接口)接口中有各种方法:创建一个停车场(工厂方法)、在某个停车位上停车、 在停车场停车,自动分配空闲停车位、将汽车驶离停车场、返回当前停车场各车位的状态、 获得车场中总的车位数目、看车场中是否存在指定车位号和宽度的车位、 当前停车场是否已满、获得宽度大于指定宽度的停车位、返回停车场的名字、返回停车场停车位的编号列表、 返回某车位宽度、获取停车记录。我们只需要在实现类中挨个重写前面的非工厂方法就好了。实现类中我们需要有停车场的名字,停车位的集合,停车场现在的状态,停车记录的集合。对于里面的所有属性,我们都应该使用private final。
private final String name;
private final Set<Lot> lots = new HashSet<>();
private final Map<Lot, Car> status = new HashMap<>();
private final Set<Record> records = new HashSet<>();
然后就是挨着的实现上述方法了,其实就没啥特别的了。后面有给了一个,这个的想法很不错,能够让输出变得规整很多。
//车位比较器,用于toString方法中按照车位号升序排序用
class lotComparator implements Comparator<Lot>{
public int compare(Lot o1,Lot o2){
/*
升序写法
< return -1
= return 0
> return 1
降序写法
< return 1
= return 0
> return -1
*/
//强制类型转换来调用函数里number变量,再来比较
int number1=((Lot)o1).getNumber();
int number2=((Lot)o2).getNumber();
if(number1==number2)
return 0;
if(number1<number2){
return -1;
}
else{
return 1;
}
}
}