续接习题课2
这个是写完lab3之后讲的,但是这个实验课的内容比lab3简单太多了!!!
在习题课2中我们实现了一个停车场类,在停车场的类中,我们实现了汽车的驶入、驶出、计费、分配车位等操作,但是停车场里面不一定只有汽车,还可能有自行车、摩托车等其他车辆,习题课指导上说还可能有飞机!!!
开始的时候我们的思路就是,用一个Parkable接口,表示所有的交通工具的一个抽象,里面有一些功能,分别是获取牌照、获取交通工具的宽度、获取计费单元、获取总的停车费用、设置交通工具的当前状态、获取交通工具的当前状态、工厂方法。
public interface Parkable {
public int getWidth();
public String getPlate();
public double getPricingUnit();
public double getPrice();
public State getState();
public void setState(State state);
public static Parkable create(String type, String plate, int width, String[] extraRegistrationInfo) throws Exception {
return new ParkableFactory().create(type, plate, width, extraRegistrationInfo);
}
}
然后的思路就很清晰了,用一个ConcreteParkable这么一个类implements这个Parkable接口,然后每一种交通工具都应该是ConcreteParkable的一个子类,在每一个子类中,我们重写个性化的方法,或者再加一些个性化的属性都可以。
比如说:计费规则不同,汽车半小时10元,飞机1小时1000元,摩托车半小时5元
public Car(String plate, int width) {//汽车半小时10元
super(plate, width);
pricingUnit = 30;
price = 10;
}
public Plane(String plate, int width) {//飞机1小时1000元
super(plate, width);
pricingUnit = 60;
price = 1000;
}
public Motor(String plate, int width) {//摩托车半小时5元
super(plate, width);
pricingUnit = 30;
price = 5;
}
后面我们使用工厂方法,然后创建对象,在Parkable接口中:
public static Parkable create(String type, String plate, int width, String[] extraRegistrationInfo) throws Exception {
return new ParkableFactory().create(type, plate, width, extraRegistrationInfo);
}
然后工厂类里面只需要根据不同的type,使用if else语句判断就好了:
public Parkable create(String type, String plate, int width) throws Exception {
if(type.equals("car"))
return new Car(plate, width);
else if (type.equals("motor"))
return new Motor(plate, width);
else if (type.equals("plane"))
return new Plane(plate, width);
else
throw new Exception("Illegal type");
}
这个里面的状态模式更是简单,在lab3中,我们的状态转移图是不一样的,所以复用性极差,甚至是不能复用,但是在这个习题课里面,每种交通工具进入停车场的状态转移图都是一样的,我们就不用区分到底是哪种具体的交通工具,只需要在ConcreteParkable类中使用状态模式就好了。
其实就是
这个非常类似我们在学状态模式的时候学的类似于自动机的状态转移,我们只需要写一个State接口,里面有park和depark操作,然后按照状态转移图在Onroad和Inpark这两个类implements这个State接口,重写park和depark操作就好了。
public interface State {//状态模式两种转换操作
State parking();
State depart();
}
public class OnroadState implements State {
static State instance = new OnroadState();
@Override
public State parking() {//onroad->inpark
return ParkingState.instance;
}
@Override
public State depart() {
throw new IllegalArgumentException();
}
public static State getInstance() {
return instance;
}
@Override
public String toString() {
return "On road";
}
}
public class ParkingState implements State {
static State instance = new ParkingState();
@Override
public State parking() {
throw new IllegalArgumentException();
}
@Override
public State depart() {//inpark->onroad
return OnroadState.instance;
}
public static State getInstance() {
return instance;
}
@Override
public String toString() {
return "In parking";
}
}
在习题课指导上,我们还要求停车场是要有公司的,由于上次的停车场没有公司这个属性,我们是在上次的基础上改动,这完全符合装饰器模式的要求,我们自然而然的就想到了使用装饰器模式。
public class ParkingFieldWithCompany extends ComplexParkingField implements ParkingField
//在里面添加一个company属性,然后重写ParkingField里面的方法就好了
同时我们想到了,停车场其实就相当于是我们买的菜,这种交通工具其实就是买菜的人,我们就可以使用访问者模式。
public class PercentageVisitor implements ParkingVisitor {
@Override
public double visit(ParkingField pf) {
int numCars = 0;
for (Integer i : pf.status().keySet()) {
if (!pf.status().get(i).equals(""))
numCars++;
}
int numLots = pf.getNumberOfLots();
double percentage = (double) numCars * 1.0 / numLots;
NumberFormat percentFormat = NumberFormat.getPercentInstance();
percentFormat.setMaximumFractionDigits(2);
percentFormat.setMinimumFractionDigits(2);
System.out.println("Parking lot is " + percentFormat.format(percentage) + " full");
return percentage;
}
}
//在ConcreteParkingField里面
@Override
public double accept(ParkingVisitor pv) {
return pv.visit(this);
}
在停车场中,我们有很多个车位,我们遍历的时候最简单的方法肯定是for循环,但是我们还有一种更好的迭代器模式,能够帮助我们实现遍历。其实就是重写next和hasNext方法。
public class ParkingIterator implements Iterator<String> {
private final Map<Lot, Parkable> parkings;
private final List<Lot> occupiedLots;
private int count = 0;
public ParkingIterator(Map<Lot, Parkable> parkings) {
this.parkings = parkings;
this.occupiedLots = new LinkedList<>(parkings.keySet());
Collections.sort(this.occupiedLots);
}
@Override
public boolean hasNext() {
return count < occupiedLots.size();
}
@Override
public String next() {
Lot l = occupiedLots.get(count);
count ++;
return parkings.get(l).toString();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
由于在习题课2中我们知道,停车是有两种停法的,一种是车主已经明确想要停的地方,如果有空闲,直接停进去就好了,另一种是车主也不知道想停在哪儿,这是我们就应该给他指定一个停车位,然后让他停进去。这块使用策略模式会非常简单。
//在ConcreteParkingField里面
@Override
public void parking(String type, String plate, int width, String[] extraRegistrationInfo) {
Parkable car;
try {
car = Parkable.create(type, plate, width, extraRegistrationInfo);
Lot lot = this.ps.selectLot(this, lots, status, car);
} catch (Exception e) {
e.printStackTrace();
}
checkRep();
}
public abstract class ParkingStrategy {
public abstract Lot selectLot(ParkingField pf, List<Lot> lots, Map<Lot, Parkable> status, Parkable c) throws Exception;
}