4 模板方法模式 - 封装算法

前言

我们知道 对重复的代码可以做抽出来,作为公共代码.但是有没有想过对 重复的逻辑(捕捉) 提取出来做公共的呢?
就比如说 :
做茶和做咖啡的步骤很相似,
做茶的步骤:1 把水煮沸 2 用沸水浸泡茶叶 3 倒进杯子 4 加柠檬
咖啡的步骤:1 把水煮沸 2 冲泡 3 倒进杯子 4 加糖和奶
如果我们能把这4个步骤提取出来,作为公共,而不做具体的实现,这就叫做算法的封装.模板方法模式就是做这个事情的.

DEMO

下面我们就依照上面的例子来做实现,我们不仅要把第一步和第三步(无论煮茶和煮咖啡都需要把水煮沸和倒进被子这2个步骤一模一样)的代码提取并实现出来,还要把这4个步骤提取出来

  • CaffeineBeverage 煮茶和煮咖啡的抽象层,公共逻辑都在这里
public abstract class CaffeineBeverage {

    final public void prepareRecipe(){ //此方法是算法的骨架,申明为final,是因为不希望子类能够修改此方法

        boilWater();    //烧水,这一步骤是公共的.

        brew(); //酿造,可能手法各有不同,需要留给子类实现

        pourInCup();

        addCondiments();    //调味品的添加,由子类实现

    }

    protected abstract void addCondiments();

    protected void pourInCup() {
        System.out.println("Pouring into Cup");
    }

    protected abstract void brew(); //protected申明,只能让 自己,同包 和 子类覆盖

    protected void boilWater() {
        System.out.println("Boiling water");
    }
}

brew和addCondiments 由子类实现

  • Tea
public class Main {

    public static void main(String[] args) {
        System.out.println("煮茶开始...");

        CaffeineBeverage caffeineBeverage = new CaffeineBeverage() {
            @Override
            protected void brew() {
                System.out.println("用沸水浸泡茶叶");
            }

            @Override
            protected void addCondiments() {
                System.out.println("加柠檬");
            }
        };
        
        caffeineBeverage.prepareRecipe();

    }

}
4 模板方法模式 - 封装算法_第1张图片
image.png
  • Coffee
public class Main {

    public static void main(String[] args) {
        System.out.println("煮咖啡开始...");

        CaffeineBeverage caffeineBeverage = new CaffeineBeverage() {
            @Override
            protected void brew() {
                System.out.println("冲泡");
            }

            @Override
            protected void addCondiments() {
                System.out.println("加糖和奶");
            }
        };

        caffeineBeverage.prepareRecipe();

    }

}
image.png

钩子

先看下钩子的用法,然后我们再总结下钩子的作用,以及什么时候使用它

public abstract class CaffeineBeverage {

    final public void prepareRecipe(){ //此方法是算法的骨架,申明为final,是因为不希望子类能够修改此方法

        boilWater();    //烧水,这一步骤是公共的.

        brew(); //酿造,可能手法各有不同,需要留给子类实现

        pourInCup();

        if(customerWantsCondiments()){  //customerWantsCondiments 代表了是否需要添加调味品,有子类自行决定,抽象层仅仅给出默认做法
            addCondiments();    //调味品的添加,由子类实现
        }

    }



    protected void pourInCup() {
        System.out.println("Pouring into Cup");
    }

    protected abstract void brew(); //protected申明,只能让 自己,同包 和 子类覆盖

    protected abstract void addCondiments();

    protected void boilWater() {
        System.out.println("Boiling water");
    }

    protected boolean customerWantsCondiments(){
        return true;
    }
}
  • 用法
public class Main {

    public static void main(String[] args) {
        System.out.println("煮咖啡开始...");

        CaffeineBeverage caffeineBeverage = new CaffeineBeverage() {
            @Override
            protected void brew() {
                System.out.println("冲泡");
            }

            @Override
            protected void addCondiments() {
                System.out.println("加糖和奶");
            }

            @Override
            protected boolean customerWantsCondiments() {
                return false;
            }
        };

        caffeineBeverage.prepareRecipe();

    }

}
image.png

1 在此我们引出 钩子的第一个作用:
钩子可以让 子类有能力为其抽象类做一些决定

2 当然,钩子的还有很多其他的用法,比如 钩子可以让子类实现算法中可选的部分,或者在钩子对于子类的实现并不重要的时候,子类可以对此钩子置之不理.

3 钩子还有一个用法,让子类能够有机会对模板方法中某些即将发生(或刚刚发生的)步骤做出反应

  • 用法如下
public abstract class CaffeineBeverage {

    final public void prepareRecipe(){ //此方法是算法的骨架,申明为final,是因为不希望子类能够修改此方法

        boilWater();    //烧水,这一步骤是公共的.

        brew(); //酿造,可能手法各有不同,需要留给子类实现

        pourInCup();

        if(customerWantsCondiments()){  //customerWantsCondiments 代表了是否需要添加调味品,有子类自行决定,抽象层仅仅给出默认做法
            addCondiments();    //调味品的添加,由子类实现
        }

        finnishHook();

    }



    protected void pourInCup() {
        System.out.println("Pouring into Cup");
    }

    protected abstract void brew(); //protected申明,只能让 自己,同包 和 子类覆盖

    protected abstract void addCondiments();

    protected void boilWater() {
        System.out.println("Boiling water");
    }

    protected boolean customerWantsCondiments(){
        return true;
    }

    protected void finnishHook(){}
}

抽象层在 算法完结时 提供了一个finishHook的方法,默认是空实现,如果子类需要在算法完结时做一些操作,可以覆盖此方法.

使用场景

模板方法模式: 我们在service层进行事务控制时,一般操作有:
1 检查参数是否存在
2 前置准备,比如检测即将删除的数据是否存在,如果不存在,就没必要往下做了..但是这个步骤是可选择的,你也可以选择不覆盖此步骤
3真正需要事务控制的这部分,我们没必要在service层引入事务管理器了,直接在模板类里引入即可
4 异常钩子,当抛出异常时,可以同时覆盖该步骤,打印出我想打印的日志

public class ServiceTemplate {
    /**
     * 当前类的日志处理器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceTemplate.class);

    /**
     * 引入事务管理器
     */
    private TransactionTemplate TransactionTemplate;

    /**
     * 事务模板方法,有返回值
     *
     * @param callBack 回调接口
     * @return RccenterCommonResult
     */
    public CommonResult executeWithTransaction(final ServiceCallBack callBack) {

        CommonResult result = new CommonResult();//统一的返回结果

        try {

            // 1.检查必要信息是否正确。
            callBack.check();

            // 2.前置准备
            callBack.prepare();

            // 3.执行服务, 开启事务。
            result = TransactionTemplate
                    .execute(new TransactionCallback() {
                        @Override
                        public CommonResult doInTransaction(TransactionStatus status) {
                            return callBack.service();
                        }

                    });

        } catch (UprcException e) { //系统自定义的异常

            LOGGER.error(
                    "系统出现业务异常:" + e.getResultCodeEnum() + ",msg=" + callBack.loggerInfoStr(), e);
            result.setResultCode(e.getResultCodeEnum());
            result.setDescription(e.getMessage());

        } catch (DataAccessException e) {

            LOGGER.error("数据库异常" + ",msg=" + callBack.loggerInfoStr(), e);
            result.setResultCode(ResultEnum.DATA_ACCESS_EXCEPTION);
            result.setDescription(e.getMessage());

        } catch (Exception e) {

            LOGGER.error("出现未知异常" + ",msg=" + callBack.loggerInfoStr(), e);
            result.setResultCode(ResultEnum.UNKNOWN_EXCEPTION);
            result.setDescription(e.getMessage());

        }

        return result;
    }

    /**
     * Setter method for property TransactionTemplate.
     *
     * @param TransactionTemplate value to be assigned to property TransactionTemplate
     */
    public void setTransactionTemplate(TransactionTemplate TransactionTemplate) {
        this.TransactionTemplate = TransactionTemplate;
    }
}
/**
 * 服务模板方法回调类
 *
 * 
 */
public interface ServiceCallBack {
    /**
     * 检查类操作
     *
     * 
     */
    void check() ;
    /**
     * 日志信息,报错时的日志信息,是一个hook
     *
     * @return
     */
    String loggerInfoStr();
    /**
     * 预处理,空实现
     */
    void prepare(){} ;

    /**
     * 实际服务操作
     *
     * @return 服务动作
     */
   CommonResult service();
}

ServiceTemplate 相当于封装了算法骨架,而ServiceCallBack 抽象了具体的步骤...我们之前的例子都是把算法骨架和步骤放在一个类里,而上面这个例子是分开的

你可能感兴趣的:(4 模板方法模式 - 封装算法)