阿里p7面试的一道题:
编程题目: 出租车起步价14元,含3公里 起步价之后,每公里2.5元
晚上11点之后(含),次日6点前(不含)起步价18元,含3公里。计价以上车时间为准,不考虑乘坐期间从白天到晚上的情况。
晚上起步价之后,每公里3元 10公里之后,白天每公里3.5元,晚上每公里4.7元
外环的出租车10公里之外的价格与10公里之内相同。外环与内环是出租车的属性,也就是说一辆外环的出租车在内环拉了客人,并行使到了外环,整段旅程都是外环的计费策略。请编写代码表示出上述的功能 要求:
- 体现出良好的设计,使得代码具有一定的灵活性和扩展性
- 提供测试代码
本文使用组合模式
考虑内外环是属性,创建枚举类; 属性内环外环;
根据题目可以看出,白天夜晚是不变的,暂时无可扩展性,价格,公里是多变的 需要一定可变性来维持,;
最终对外提供的接口 一定是这样的:
@Service
public class TaxiPriceServiceImpl implements TaxiPriceService, ApplicationContextAware {
@Autowired
private ApplicationContext applicationContext;
@Override
public BigDecimal calculatePrice(Date abroadTime, BigDecimal distance, Taxi taxi) {
List<AbstractDistanceHandler> distanceHandlerList = new ArrayList<>();
if (distance.compareTo(Distance.STARTING_DISTANCE) <= BigDecimal.ZERO.intValue()) { //小于三公里的金钱
distanceHandlerList.add(applicationContext.getBean(StartingDistanceHandler.class));
} else if (distance.compareTo(Distance.ADJUST_DISTANCE) <= BigDecimal.ZERO.intValue()) { //小于10公里的金钱
distanceHandlerList.add(applicationContext.getBean(StartingDistanceHandler.class));
distanceHandlerList.add(applicationContext.getBean(BeforeAdjustHandler.class));
} else { //大于10公里的金钱
distanceHandlerList.add(applicationContext.getBean(StartingDistanceHandler.class));
distanceHandlerList.add(applicationContext.getBean(BeforeAdjustHandler.class));
distanceHandlerList.add(applicationContext.getBean(AfterAdjustHandler.class));
}
//取到金钱list 开始相加
BigDecimal totalPrice = BigDecimal.ZERO;
if (!CollectionUtils.isEmpty(distanceHandlerList)) {
for (AbstractDistanceHandler distanceHandler : distanceHandlerList) {
totalPrice = totalPrice.add(distanceHandler.calculatePrice(abroadTime, distance, taxi));
System.out.println("总和"+totalPrice);
}
}
return totalPrice;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
开始楼代码: 创建一个抽象类,出租车类
/** 出租类
* Created by z1761 on 2018/10/11.
*/
public abstract class AbstractDistanceHandler {
public BigDecimal calculatePrice(Date abroadTime, BigDecimal distance, Taxi taxi) {
BigDecimal multiply = this.getUnitPrice(abroadTime, taxi).multiply(this.calculateDistance(distance));
System.out.println("计算出 单价和公里相乘的价格"+multiply);
return multiply;
}
public abstract BigDecimal getUnitPrice(Date abroadTime, Taxi taxi); //每公里的单价
public abstract BigDecimal calculateDistance(BigDecimal distance); //行驶距离
}
三个实现类,分别是3公里内,10公里内,10公里外
/** 起步价
* Created by z1761 on 2018/10/11.
*/
@Service
public class StartingDistanceHandler extends AbstractDistanceHandler {
@Autowired
AbroadTimeService abroadTimeServicess;
@Autowired
ApplicationContext applicationContextss;
@Override
public BigDecimal getUnitPrice(Date abroadTime, Taxi taxi) {
AbroadTimeService bean = applicationContextss.getBean(AbroadTimeService.class);
AbroadTimeServiceImpl abroadTimeService = new AbroadTimeServiceImpl();
//AbroadTimeTypeEnum abroadTimeType = abroadTimeService.determineAbroadTimeType(abroadTime);
//计算时间是白天还是晚上
AbroadTimeTypeEnum abroadTimeType = this.abroadTimeServicess.determineAbroadTimeType(abroadTime);
if (AbroadTimeTypeEnum.DAY.equals(abroadTimeType)) {
return Price.DAY_STARTING_PRICE;
}
if (AbroadTimeTypeEnum.NIGHT.equals(abroadTimeType)) {
return Price.NIGHT_STARTING_PRICE;
}
return BigDecimal.ZERO;
}
@Override
public BigDecimal calculateDistance(BigDecimal distance) {
return BigDecimal.ONE;
}
}
十公里内
/** 大于三公里 小于10公里
* Created by z1761 on 2018/10/11.
*/
@Service
public class BeforeAdjustHandler extends AbstractDistanceHandler {
@Autowired
private AbroadTimeService abroadTimeService;
@Override
public BigDecimal getUnitPrice(Date abroadTime, Taxi taxi) {
//计算时间是白天还是晚上
AbroadTimeTypeEnum abroadTimeType = this.abroadTimeService.determineAbroadTimeType(abroadTime);
if (AbroadTimeTypeEnum.DAY.equals(abroadTimeType)) {
return Price.DAY_UNIT_PRICE_BEFORE_ADJUST;
}
if (AbroadTimeTypeEnum.NIGHT.equals(abroadTimeType)) {
return Price.NIGHT_UNIT_PRICE_BEFORE_ADJUST;
}
return BigDecimal.ZERO;
}
@Override
public BigDecimal calculateDistance(BigDecimal distance) {
if (distance.compareTo(BigDecimal.TEN)>0) {
return new BigDecimal(BigDecimal.ROUND_UNNECESSARY);
}
return distance.subtract(Distance.STARTING_DISTANCE);
}
}
十公里外,看提所示 外环和内环 10公里内白天夜晚价格一样,只有10公里之外价格才不一样
/** 10公里之后的价格
* Created by z1761 on 2018/10/11.
*/
@Service
public class AfterAdjustHandler extends AbstractDistanceHandler {
@Autowired
private AbroadTimeService abroadTimeService;
@Override
public BigDecimal getUnitPrice(Date abroadTime, Taxi taxi) {
//计算时间是白天还是晚上
AbroadTimeTypeEnum abroadTimeType = this.abroadTimeService.determineAbroadTimeType(abroadTime);
TravelScopeEnum travelScope = taxi.getTravelScope();
//判断是白天并且是内换车
if (AbroadTimeTypeEnum.DAY.equals(abroadTimeType) && TravelScopeEnum.INNER_LOOP.equals(travelScope)) {
return Price.DAY_UNIT_PRICE_AFTER_ADJUST;
}
//判断是白天并且是外环车
if (AbroadTimeTypeEnum.DAY.equals(abroadTimeType) && TravelScopeEnum.OUTER_LOOP.equals(travelScope)) {
return Price.DAY_UNIT_PRICE_BEFORE_ADJUST;
}
//判断是晚上并且是内环车
if (AbroadTimeTypeEnum.NIGHT.equals(abroadTimeType) && TravelScopeEnum.INNER_LOOP.equals(travelScope)) {
return Price.NIGHT_UNIT_PRICE_AFTER_ADJUST;
}
//判断是晚上并且是外车
if (AbroadTimeTypeEnum.NIGHT.equals(abroadTimeType) && TravelScopeEnum.OUTER_LOOP.equals(travelScope)) {
return Price.NIGHT_UNIT_PRICE_BEFORE_ADJUST;
}
return BigDecimal.ZERO;
}
@Override
public BigDecimal calculateDistance(BigDecimal distance) {
return distance.subtract(Distance.ADJUST_DISTANCE);
}
}
每个类里都有计算时间的接口
/** 计算时接口
* Created by z1761 on 2018/10/11.
*/
public interface AbroadTimeService {
AbroadTimeTypeEnum determineAbroadTimeType(Date abroadTime);
boolean isPeak(Date abroadTime);
}
impl
/** 计算白天还是黑夜
* Created by z1761 on 2018/10/12.
*/
@Service
public class AbroadTimeServiceImpl implements AbroadTimeService {
@Override
public AbroadTimeTypeEnum determineAbroadTimeType(Date abroadTime) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH");
String format = simpleDateFormat.format(date);
Integer hour = Integer.valueOf(format);
if (hour > AbroadTime.DAY_START || hour < AbroadTime.DAY) {
return null;
}
//大于六点 小于23点 是在白天
if (hour> AbroadTime.DAY_START&&hour<=AbroadTime.NIGHT_START) {
return AbroadTimeTypeEnum.DAY;
} else if (hour<=AbroadTime.DAY_START||hour<AbroadTime.NIGHT_START) {
return AbroadTimeTypeEnum.NIGHT;
}
return null;
}
@Override
public boolean isPeak(Date abroadTime) {
return false;
}
}
因为只是一个demo 所有没有做null判断;
三个 constant ,价格,公里,时间,
/** 时间
* Created by z1761 on 2018/10/11.
*/
public class AbroadTime {
public static final int DAY_START = 6;
public static final int NIGHT_START = 23;
public static final int DAY = 0;
public static final int NIGHT=24;
}
/** 公里
* Created by z1761 on 2018/10/11.
*/
public class Distance {
public static BigDecimal STARTING_DISTANCE = new BigDecimal(3);
public static BigDecimal ADJUST_DISTANCE = new BigDecimal(10);
}
/** 价格
* Created by z1761 on 2018/10/11.
*/
public class Price {
public static final BigDecimal DAY_STARTING_PRICE = new BigDecimal(14);
public static final BigDecimal DAY_UNIT_PRICE_BEFORE_ADJUST = new BigDecimal(2.5);
public static final BigDecimal DAY_UNIT_PRICE_AFTER_ADJUST = new BigDecimal(3.5);
public static final BigDecimal NIGHT_STARTING_PRICE = new BigDecimal(18);
public static final BigDecimal NIGHT_UNIT_PRICE_BEFORE_ADJUST = new BigDecimal(3);
public static final BigDecimal NIGHT_UNIT_PRICE_AFTER_ADJUST = new BigDecimal(4.7);
}
如果需求有点改变,或者出现高峰期的价格波动,则采用装饰者模式+组合模式;
/** 如果是高峰期 按照高峰期来算 ,早上9. 10点 晚上 7-9点
* 这个时间段都在白天路段。
* 举例 10公里内的
* @Primary 这个注解告诉spring 假如有多个实现或者继承的话,先去这里找
* Created by z1761 on 2018/10/11.
*/
@Component
@Primary
public class BeforeAdjustHandlerDecorator extends AbstractDistanceHandler {
@Autowired
private AbroadTimeService abroadTimeService;
@Autowired
private BeforeAdjustHandler beforeAdjustHandler;
@Override
public BigDecimal getUnitPrice(Date abroadTime, Taxi taxi) {
//在这里进行判断 时间是否在高峰期内,在的话 return单价,假设高峰期单价为TEN(10)
if (abroadTimeService.isPeak(abroadTime)) {
return BigDecimal.TEN;
}
//不在的话 高峰期又在白天内; 所已调用
return this.beforeAdjustHandler.getUnitPrice(abroadTime, taxi);
}
@Override
public BigDecimal calculateDistance(BigDecimal distance) {
return this.beforeAdjustHandler.calculateDistance(distance);
}
}