DesignPattern-行为型-模板模式

1、简介

  在父类中定义一个操作中的算法的骨架,而将一些步骤声明为抽象方法,使其延迟到子类中。

  主要用于一些方法通用,却在每一个子类都重新写了这一方法的情形。

  模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

  优点: 

    1、封装不变部分,扩展可变部分。

    2、提取公共代码,便于维护。

    3、行为由父类控制,子类实现。

  缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

2、实例

  例举一个之前实现的航班行李计算器。

  其中,国际航班的行李计价流程一致,但是分为五个区域,每个区域的计价规则略有不同。

  简单的思维导图如下:

    

package com.example.practice1.service;

import com.example.practice1.dao.BaggageType;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Lqb
 * 计算国际行李额的抽象类,
 * 使用模板方法,定义了国际行李额的计算方法
 * 具体的超限行李额的计算规则声名为抽象方法,须在子类中实现
 */
public abstract class ICalculateBaggageFee {

    /**
     * 获取免费行李额
     * @param information 行李信息
     * @return 免费行李额
     */
    public static FreeLimit iCalculateFreeLimit(BaggageInformation information){
        FreeLimit freeLimit=new FreeLimit(information.getPassengerType(), information.getCabinClass());

        //get extra limit
        freeLimit.addItemLimit(information.getCabinClass().getFreeItem());
        freeLimit.addWeightLimit(information.getCabinClass().getFreeWeight());

        return freeLimit;
    }

    /**
     * 扣除免费行李额
     *    超重的行李要付超限额,不能减去免费行李重量
     * @param allBaggages 能够使用免费行李额的行李
     * @param freeLimit 免费行李额
     * @return 付费详情列表
     */
    public static List<BaggageFee> useLimit(List<Baggage> allBaggages, FreeLimit freeLimit){
        System.out.println("Free Limit: "+freeLimit.getCabinClass());

        List<Baggage> baggages = allBaggages
                .stream()
                .filter(i->i.getBaggageType().getIndex()<5)
                .collect(Collectors.toList());
        List<Baggage> baggage=baggages
                .stream()
                .filter(i-> i.getBaggageWeight()<=freeLimit.getCabinClass().getFreeWeight()
                        && i.getBaggageSize()<=158)
                .limit((long) freeLimit.getFreeItems())
                .collect(Collectors.toList());
        List<BaggageFee> baggageFees=baggage
                .stream()
                .map(i->new BaggageFee(0,"Free Allowance: "+i.getBaggageWeight()))
                .collect(Collectors.toList());
        //去掉免费额度中的行李
        allBaggages.removeAll(baggage);

        System.out.println("useLimit"+baggage);

        return baggageFees;
    }

    /**
     * 扣除免费托运的特殊行李
     * @param allBaggages 所有行李信息
     * @return 付费详情列表
     */
    public static List<BaggageFee> handleFree(List<Baggage> allBaggages){

        List<Baggage> freeBaggage= allBaggages
                .stream()
                .filter(i->i.getBaggageType()== BaggageType.free)
                .collect(Collectors.toList());
        List<BaggageFee> baggageFees=freeBaggage
                .stream()
                .map(i->new BaggageFee(0,"Free Baggage: "+i.getBaggageWeight()))
                .collect(Collectors.toList());
        //去掉免费行李
        allBaggages.removeAll(freeBaggage);
        return baggageFees;
    }

    /**
     * 计算超重的超限额
     * 须在子类中实现
     * @param baggages 去除了免费行李剩下的超重的行李
     * @return 付费详情列表
     */
    public abstract List<BaggageFee> getOverWeightFee(List<Baggage> baggages);

    /**
     * 计算超尺寸的超限额
     * 注意:对于超出尺寸的特殊行李不用付超限额
     * 须在子类中实现
     * @param baggages 去除了免费行李剩下的超尺寸的行李
     * @return 付费详情列表
     */
    public abstract List<BaggageFee> getOverSizeFee(List<Baggage> baggages);

    /**
     * 计算超出重量和尺寸的超限额
     * 注意:对于超出尺寸的特殊行李不用付超限额
     * 须在子类中实现
     * @param baggages 去除了免费行李剩下的超出重量和尺寸的行李列表
     * @return 付费详情列表
     */
    public abstract List<BaggageFee> getOverWeightAndSizeFee(List<Baggage> baggages);

    /**
     * 计算超出件数的超限额
     * 注意:该函数需在扣除免费额度之前调用。
     * 须在子类中实现
     * @param baggages 去除了免费行李剩下的全部行李
     * @return 付费详情列表
     */
    public abstract List<BaggageFee> getOverItemFee(List<Baggage> baggages, FreeLimit freeLimit);

    /**
     * 计算特殊行李的托运费用
     * @param baggages 不能计入免费行李额的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageFee(List<Baggage> baggages){
        List<BaggageFee> baggageFees=getCanNotBeFreeBaggageSport1Fee(baggages);
        baggageFees.addAll(getCanNotBeFreeBaggageSport2Fee(baggages));
        baggageFees.addAll(getCanNotBeFreeBaggageOther1Fee(baggages));
        baggageFees.addAll(getCanNotBeFreeBaggageOther2Fee(baggages));
        baggageFees.addAll(getCanNotBeFreeBaggageOther3Fee(baggages));
        baggageFees.addAll(getCanNotBeFreeBaggageOther4Fee(baggages));

        return baggageFees;
    }

    /**
     * 计算不能计入免费额度的体育器械的费用,体育用品第一类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageSport1Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeSport1)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=23)
                        return new BaggageFee(2600,"Exercisers 2-23KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=32)
                        return new BaggageFee(3900,"Exercisers 23-32KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=45)
                        return new BaggageFee(5200,"Exercisers 32-45KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >45kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算不能计入免费额度的体育器械的费用,体育用品第二类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageSport2Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeSport2)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=23)
                        return new BaggageFee(1300,"Exercisers 2-23KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=32)
                        return new BaggageFee(2600,"Exercisers 23-32KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=45)
                        return new BaggageFee(3900,"Exercisers 32-45KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >45kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算不能计入免费额度的其他特殊行李的费用,电器类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageOther1Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeOther1)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=23)
                        return new BaggageFee(490,"Electrical Appliances 2-23KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=32)
                        return new BaggageFee(3900,"Electrical Appliances 23-32KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >32kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算不能计入免费额度的其他特殊行李的费用,枪支类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageOther2Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeOther2)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=23)
                        return new BaggageFee(1300,"Guns 2-23KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=32)
                        return new BaggageFee(2600,"Guns 23-32KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >32kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算不能计入免费额度的其他特殊行李的费用,电子弹类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageOther3Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeOther3)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=5)
                        return new BaggageFee(1300,"Bullets 2-5KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >5kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算不能计入免费额度的其他特殊行李的费用,动物类
     * @param baggages 所有不能免费托运的特殊行李
     * @return 付费详情列表
     */
    public static List<BaggageFee> getCanNotBeFreeBaggageOther4Fee(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType()==BaggageType.sCanNotBeFreeOther4)
                .map(i->{
                    if(i.getBaggageWeight()<2)
                        return new BaggageFee(0,"Can Not Be Transported: <2kg");
                    else if(i.getBaggageWeight()<=8)
                        return new BaggageFee(3900,"Animals 2-8KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=23)
                        return new BaggageFee(5200,"Animals 8-23KG: "+i.getBaggageWeight());
                    else if(i.getBaggageWeight()<=32)
                        return new BaggageFee(7800,"Animals 23-32KG: "+i.getBaggageWeight());
                    return new BaggageFee(0,"Can Not Be Transported: >32kg");
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取所有行李
     * @param information 用户输入的信息
     * @return 所有行李,包括特殊行李
     */
    public static List<Baggage> getAllBaggage(BaggageInformation information){
        List<Baggage> baggages=information.getBaggages();
        baggages.addAll(information.getSBaggages());
        return baggages;
    }

    /**
     * 获取能计入免费额度的所有行李,包括特殊行李
     * @param baggages 所有行李
     * @return 能使用免费额度的行李列表
     */
    public static List<Baggage> getCanBeFreeBaggage(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType().getIndex()<4)
                .collect(Collectors.toList());
    }

    /**
     * 获取不能计入免费额度的特殊行李
     * @param baggages 所有行李
     * @return 不能使用免费额度的行李列表
     */
    public static List<Baggage> getCanNotBeFreeBaggage(List<Baggage> baggages){
        return baggages.stream().filter(i->i.getBaggageType().getIndex()>=5).collect(Collectors.toList());
    }

    /**
     * 获取超重的行李
     * @param baggages 所有行李
     * @return 超重的行李列表
     */
    public static List<Baggage> getOverWeightBaggage(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType().getIndex()<5)
                .filter(i->i.getBaggageWeight()>23&&(i.getBaggageSize()<=158||i.getBaggageType()==BaggageType.sCanBeFree))
                .peek(i-> System.out.println("OverWeight: "+i))
                .collect(Collectors.toList());
    }

    /**
     * 获取超尺寸的行李
     * @param baggages 所有行李
     * @return 超尺寸的行李列表
     */
    public static List<Baggage> getOverSizeBaggage(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType().getIndex()<5)
                .filter(i->i.getBaggageSize()>158&&i.getBaggageWeight()<=23)
                .peek(i-> System.out.println("OverSize: "+i))
                .collect(Collectors.toList());
    }

    /**
     * 获取既超重又超尺寸的行李
     * @param baggages 能计入免费额度的行李,不包括免费运输的行李
     * @return 超尺寸的行李列表
     */
    public static List<Baggage> getOverSizeAndWeightBaggage(List<Baggage> baggages){
        return baggages
                .stream()
                .filter(i->i.getBaggageType().getIndex()<5)
                .filter(i->i.getBaggageSize()>158&&i.getBaggageWeight()>23)
                .peek(i-> System.out.println("OverWeightAndSize: "+i))
                .collect(Collectors.toList());
    }

    /**
     * 计算行李额
     * @param information 行李信息
     * @return 行李收费列表
     */
    public List<BaggageFee> calculateBaggageFee(BaggageInformation information){
        List<Baggage> allBaggages=getAllBaggage(information);
        //按从小到大排序
        allBaggages=allBaggages
                .stream()
                .sorted(Comparator.comparing(Baggage::getBaggageWeight).reversed())
                .collect(Collectors.toList());

        System.out.println("Sorted Baggage:"+allBaggages);

        FreeLimit freeLimit=iCalculateFreeLimit(information);

        //免费托运的行李
        List<BaggageFee> baggageFees=new ArrayList<>(handleFree(allBaggages));

        //超出件数,特殊行李单独付款,没有放在超出件数里面
        baggageFees.addAll(getOverItemFee(getCanBeFreeBaggage(allBaggages), freeLimit));

        System.out.println("Before useLimit:"+allBaggages);

        //使用免费额度
        baggageFees.addAll(useLimit(allBaggages, freeLimit));

        System.out.println("After useLimit:"+allBaggages);

        //超重
        baggageFees.addAll(getOverWeightFee(getOverWeightBaggage(allBaggages)));

        //超尺寸
        baggageFees.addAll(getOverSizeFee(getOverSizeBaggage(allBaggages)));

        //超重&&超尺寸
        baggageFees.addAll(getOverWeightAndSizeFee(getOverSizeAndWeightBaggage(allBaggages)));

        //不能计入免费的特殊行李
        baggageFees.addAll(getCanNotBeFreeBaggageFee(getCanNotBeFreeBaggage(allBaggages)));

        System.out.println("Baggage Fees: "+baggageFees);
        return baggageFees;
    }

    /**
     * 根据收费列表计算出行李收费总额
     * @param baggageFees 行李收费列表
     * @return 行李收费总额
     */
    public static BaggageFee getAllBaggageFee(List<BaggageFee> baggageFees){
        return baggageFees
                .stream()
                .reduce((i,j)->new BaggageFee(i.getTotalFee()+j.getTotalFee(),"Total Fee"))
                .orElseGet(() -> new BaggageFee(0, "None"));
    }
}

  一个区域的实现:

package com.example.practice1.service;

import com.example.practice1.dao.BaggageType;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.*;

/**
 * @author Lqb
 * @see com.example.practice1.service.ICalculateBaggageFee
 * 计算区域一行李费用
 */
public class ICBFRegion1 extends ICalculateBaggageFee{

    /**
     * @see com.example.practice1.service.ICalculateBaggageFee
     * @param baggages 超重的行李
     * @return
     */
    @Override
    public List<BaggageFee> getOverWeightFee(List<Baggage> baggages) {
//        Map<String, List<Baggage>> overWeight=baggages.stream().collect(groupingBy(
//                i->{
//                    if(i.getBaggageWeight()<=28)
//                        return "overWeight1";
//                    else
//                        return "overWeight2";
//                }));


        List<BaggageFee> baggageFees= baggages
                .stream()
                .filter(i->i.getBaggageWeight()<=28)
                .map(i->new BaggageFee(380,"Region1 OverWeight: 380 for "+i.getBaggageWeight()))
                .collect(toList());

        baggageFees.addAll(baggages
                .stream()
                .filter(i->i.getBaggageWeight()>28)
                .map(i->{
                    if(i.getBaggageWeight()<=32)
                        return new BaggageFee(980,"Region1 OverWeight: 980 for "+i.getBaggageWeight());
                    else
                        return new BaggageFee(0,">32kg Can Not Be Transported!");
                })
                .collect(toList()));

        return baggageFees;
    }

    @Override
    public List<BaggageFee> getOverSizeFee(List<Baggage> baggages) {

        return baggages
                .stream()
                //可计入免费额度的特殊行李超尺寸不另收费
                .filter(i->i.getBaggageType()!= BaggageType.sCanBeFree)
                .map(i->new BaggageFee(980,"Region1 OverSize: 980 for "+i.getBaggageSize()))
                .collect(toList());
    }

    @Override
    public List<BaggageFee> getOverWeightAndSizeFee(List<Baggage> baggages) {
        return baggages
                .stream()
                //可计入免费额度的特殊行李超尺寸不另收费
                .filter(i->i.getBaggageType()!= BaggageType.sCanBeFree)
                .map(i->new BaggageFee(1400,"Region1 OverWeight And OverSize: 1400 for "+i.getBaggageWeight()+" kg and "+i.getBaggageSize()+" cm"))
                .collect(toList());
    }

    @Override
    public List<BaggageFee> getOverItemFee(List<Baggage> baggages, FreeLimit freeLimit) {
        List<BaggageFee> baggageFees=new ArrayList<>();
        if(baggages.size()-freeLimit.getFreeItems()<=0) return baggageFees;

        //第一件
        baggageFees.add(new BaggageFee(1400, "First Baggage Over Limit"));
        if(baggages.size()-freeLimit.getFreeItems()<=1) { return baggageFees; }

        //第二件
        baggageFees.add(new BaggageFee(2000, "Second Baggage Over Limit"));
        if(baggages.size()-freeLimit.getFreeItems()<=2) { return baggageFees; }

        //第三件及以上
        for(int i=0; i<baggages.size()-freeLimit.getFreeItems()-3+1; i++)
            baggageFees.add(new BaggageFee(3000, "Third Or More Baggage Over Limit"));
        return baggageFees;

    }
}

 

3、思考和改进

  设计模式只是一些典型场景下的经典实现方法,各个模式之间的界限不是那么清晰的。

  从骨架和子类填充的角度看,这里是模板方法。

  从五个区域,不同区域不同策略的角度看,这里使用了策略模式。

  本来就是前人经验总结,管他是什么模式,好用就行。

  这里产生了五个子类,子类多了实例化就比较复杂了。那么,为什么不将实例化分离出来呢——简单工厂模式!

  通过区域来返回处理类,这样就方便了。

package com.example.practice1.service;

import com.example.practice1.dao.Region;

/**
 * @author Lqb
 * 工厂方法,根据区域,返回对应的行李费用计算类
 */
public class CalculateBaggageFeeFactory {
    public static ICalculateBaggageFee getInternationalBaggageFeeCalculator(Region region){
        switch (region){
            case region1: {
                return new ICBFRegion1();
            }
            case region2:{
                return new ICBFRegion2();
            }
            case region3:{
                return new ICBFRegion3();
            }
            case region4:{
                return new ICBFRegion4();
            }
            case region5:{
                return new ICBFRegion5();
            }
            default:
                return null;
        }
    }
}

 

posted @ 2021-10-04 14:44  Lqblalala  阅读(44)  评论(0编辑  收藏  举报