zoukankan      html  css  js  c++  java
  • 【设计模式】模板方法

    一、前言

      最近复习发现AQS使用了模板方法,自定义同步器时需要重写几个AQS提供的模板方法,Spring的DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions() 方法也使用了该设计模式,于是写篇文章加深理解。

    模板方法模式的定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

    单看这句话可能还没搞懂这个设计模式是干嘛的,下面看一个例子。

    二、示例

    不用设计模式,冲咖啡和泡茶的两个类分别是下面这样的。

    public class Coffee {
    
      // 冲泡咖啡的算法
       void prepareRecipe(){
        boilWater();
        // 用沸水冲泡咖啡
        brewCoffeeGrinds();
        pourInCup();
        // 加牛奶和糖
        addSugarAndMilk();
      }
    
      private void addSugarAndMilk() {
      }
    
      private void pourInCup() {
      }
    
      private void brewCoffeeGrinds() {
      }
    
      private void boilWater() {
      }
    }
    
    
    public class Tea {
    
      // 泡茶的算法
      void prepareRecipe(){
        boilWater();
        // 用沸水浸泡茶叶
        steepTeaBag();
        pourInCup();
        // 加柠檬
        addLemon();
      }
    
      private void addLemon() {
    
      }
    
      private void pourInCup() {
    
      }
    
      private void steepTeaBag() {
    
      }
    
      private void boilWater() {
    
      }
    }
    

    很容易就可以发现这两个类中有重复代码,boilWater()和pourInCup()都重复了可以提取出来,因为茶和饮料都是咖啡饮料可以定义一个超类CaffeineBeverage。
    进一步分析,两种冲泡法其实都用了相同的算法:

    1. 把水煮沸
    2. 用热水泡咖啡或茶
    3. 把饮料倒进杯子
    4. 在饮料里加调料

    所以prepareRecipe()也可以抽象成一个,如下:

    final void prepareRecipe(){
        boilWater();
        // 交给对应的子类实现
        brew();
        pourInCup();
        // 交给对应的子类实现
        addCondiments();
      }
    

    最后,重构后的代码如下。

    咖啡因饮料超类

    public abstract class CaffeineBeverage {
    
      /**
       * 模板方法
       * 定义的算法步骤
       */
      final void prepareRecipe(){
        boilWater();
        brew();
        pourInCup();
        addCondiments();
      }
    
      /**
       * 添加调料
       */
      protected abstract void addCondiments();
    
    
      /**
       * 冲泡
       */
      protected abstract void brew();
    
      /**
       * 煮开水
       */
      private void boilWater() {
        System.out.println("Boiling water");
      }
    
      /**
       * 把饮料倒进杯子
       */
      private void pourInCup() {
        System.out.println("Pouring in cup");
      }
    
    }
    
    

    Tea

    public class Tea extends CaffeineBeverage {
    
      @Override
      protected void addCondiments() {
        System.out.println("Adding Lemon");
      }
    
      @Override
      protected void brew() {
        System.out.println("Steeping the tea");
      }
    }
    

    Coffee

    public class Coffee extends CaffeineBeverage {
    
      @Override
      protected void addCondiments() {
        System.out.println("Adding Sugar and Milk ");
      }
    
      @Override
      protected void brew() {
        System.out.println("Dripping Coffee through filter");
      }
    }
    

    代码的类图就变成了下面的样子。

     

    类图
    类图

     

    道理我懂,可是使用了模板方法模式之后有什么好处吗?

    原本的实现模板方法后的实现
    Coffee和Tea之间存在重复代码 CaffeineBeverage类实现了代码复用最大化
    Coffee和Tea控制了算法 由CaffeineBeverage类主导一切,拥有并保护算法
    对算法所做的代码改变,需要修改子类很多地方 算法只存在一个地方,很容易修改
    算法的知识和它的实现分散在许多类中 CaffeineBeverage类专注算法本身,而由子类提供完整的实现

    三、总结

    设计模式这么多种,要做到灵活运用还真是长路漫漫啊。

  • 相关阅读:
    Java 跨系统开发隐患(一)
    SpringBoot邮件推送功能
    基于图灵api的Python机器人
    JSP编码问题解决方法
    记一次数据结构课设
    基于百度语音识别API的Python语音识别小程序
    帝国cms过滤采集内容
    如何批量取消文章审核
    评论时判断会员是否登录
    帝国cms把文章加入到收藏夹代码
  • 原文地址:https://www.cnblogs.com/2YSP/p/11627282.html
Copyright © 2011-2022 走看看