美文网首页
4 模板方法模式 - 封装算法

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

作者: 6cc89d7ec09f | 来源:发表于2018-10-17 13:49 被阅读31次

前言

我们知道 对重复的代码可以做抽出来,作为公共代码.但是有没有想过对 重复的逻辑(捕捉) 提取出来做公共的呢?
就比如说 :
做茶和做咖啡的步骤很相似,
做茶的步骤: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();

    }

}
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<CommonResult>() {
                        @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 <tt>TransactionTemplate</tt>.
     *
     * @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 抽象了具体的步骤...我们之前的例子都是把算法骨架和步骤放在一个类里,而上面这个例子是分开的

相关文章

  • 各种设计模式总结和对比

    模板方法模式和策略模式 1、模板方法和策略模式都有封装算法。2、策略模式是使不同算法可以相互替换,且不影响客户端应...

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

    前言 我们知道 对重复的代码可以做抽出来,作为公共代码.但是有没有想过对 重复的逻辑(捕捉) 提取出来做公共的呢?...

  • iOS设计模式 ─── 算法封装

    算法封装,通过封装和拓展对象的算法来改变对象的行为。涉及到算法封装的有以下三种设计模式:① 模板方法(封装共用行为...

  • 设计模式系列教程—Template Method Pattern

    9 Template Method Pattern(模板方法模式) 前言:封装步骤的算法。Vander作为老板,凡...

  • Java 设计模式(9) —— 模板模式

    一、模板模式 封装了一个算法步骤,并允许子类为一个或多个步骤方法提供实现。模板模式可以使子类在不改变算法结构的情况...

  • 设计模式系列-模板方法模式

    JAVA设计模式系列: 单例模式 观察者模式 模板方法模式 模板方法模式 定义 模板方法模式在一个方法中定义了算法...

  • JavaScript模板方法模式

    在静态语言中,实现模板方法模式通过继承实现。通过抽象父类封装子类的算法框架——包括实现一些公共方法以及封装子类中所...

  • 8、模板方法模式-封装算法

  • 大话设计模式之模板方法模式

    模板方法模式 模板方法模式: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的...

  • 模板方法模式

    模板方法模式定义 模板方法(Template Method)模式定义一个操作中的算法骨架,而将算法的一些步骤延迟到...

网友评论

      本文标题:4 模板方法模式 - 封装算法

      本文链接:https://www.haomeiwen.com/subject/yonlzftx.html