设计模式之 - 模板模式

简介

某个业务流程中,有些步骤或内容是固定不变的,那么这些步骤或内容就没有必要每次实现的时候都去重复实现,而应该是设计成模板,让使用者直接使用即可。而不能确定的步骤或内容,又允许让使用者根据自己的需求去实现,这就是“模板模式”的设计思想

在 Java 编程中,模板设计模式是通过抽象类来实现的,因为抽象类体现的就是“部分实现,部分抽象”这样的思想。

模板模式的好处:可以极大的简化代码和提高代码的重用性,从而提高开发效率。

模板方法(抽象类中已经实现的方法)通常会加上 final 关键字,防止被子类重写。

模板模式使用场景:当要完成某个过程,该过程要执行一系列的步骤,这一系列的步骤是固定不变的,只是个别步骤中的具体实现因为不同的业务有所不同,这种情况可以考虑使用模板模式来开发。

演示案例

案例一(制作菜品)

制作菜品的流程都相同:准备原材料、烹饪、上菜,只是三个步骤中的具体内容不同而已,那么就可以将制作菜品这件事抽象成一个抽象类,而三个步骤则抽象成三个抽象方法,并且再将制作菜品的标准流程抽象成一个具体方法,这个具体方法中去运行三个步骤对应的方法。

那么制作西红柿炒蛋这件事也抽象成一个类,这个类去继承抽象类,然后实现其中的三个抽象方法(步骤方法);同样地制作葱花豆腐这件事也抽象成一个类,也去继承抽象类,实现其中的三个抽象方法。那么抽象类中的具体方法也被继承到实现子类中了,也就是说西红柿炒蛋这个类就拥有了制作菜品的方法了,葱花豆腐也拥有了制作菜品的方法,需要制作菜品时就只要调用制作菜品的方法就可以了。

注:在这个案例中,可以将抽象类理解为模板,而抽象类中的具体方法理解为模板方法。

模板抽象类的代码:

package priv.lwx.design.template;

/**
 * 设计模式之 - 模板模式的演示
 * 该抽象类用来设计做菜品的标准流程(制作菜品的流程模板)
 *
 * @author liaowenxiong
 * @date 2022/6/5 16:32
 */

public abstract class Cooking {
    
    
  /**
   * 准备材料
   *
   * @param
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/5 16:39
   */
  public abstract String[] prepareMaterials();

  /**
   * 烹饪
   *
   * @param materials
   * @return String
   * @throws
   * @author liaowenxiong
   * @date 2022/6/5 16:58
   */
  public abstract String cook(String[] materials);

  /**
   * 上菜
   *
   * @param dish
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/5 16:59
   */
  public abstract void serveDishes(String dish);

  /**
   * 制作菜品
   *
   * @param
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/5 16:59
   */
  public void makeDish() {
    
    
    String[] materials = prepareMaterials();
    String dish = cook(materials);
    serveDishes(dish);
  }
}

制作西红柿炒蛋的代码:

package priv.lwx.design.service;

import priv.lwx.design.template.Cooking;

/**
 * 制作番茄炒蛋
 *
 * @author liaowenxiong
 * @date 2022/6/5 21:04
 */

public class TomatoOmelette extends Cooking {
    
    
  /**
   * 准备食材
   *
   * @param
   * @return String[]
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 10:19
   */
  @Override
  public String[] prepareMaterials() {
    
    
    return new String[]{
    
    "tomato", "omelette"};
  }

  /**
   * 烹饪
   *
   * @param materials
   * @return String
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 10:19
   */
  @Override
  public String cook(String[] materials) {
    
    
    String dish = "";
    for (int i = 0; i < materials.length; i++) {
    
    
      if (i == materials.length - 1) {
    
    
        dish += materials[i];
      } else {
    
    
        dish += materials[i] + "+";
      }
    }
    return dish;
  }

  /**
   * 上菜
   *
   * @param dish
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 10:21
   */
  @Override
  public void serveDishes(String dish) {
    
    
    System.out.println(dish);
  }
}

制作葱花豆腐的代码:

package priv.lwx.design.service;

import priv.lwx.design.template.Cooking;

/**
 * 制作葱花豆腐
 *
 * @author liaowenxiong
 * @date 2022/6/6 10:33
 */

public class ScallionTofu extends Cooking {
    
    
  @Override
  public String[] prepareMaterials() {
    
    
    return new String[]{
    
    "scallion", "tofu"};
  }

  @Override
  public String cook(String[] materials) {
    
    
    String dish = "";
    for (int i = 0; i < materials.length; i++) {
    
    
      if (i == materials.length - 1) {
    
    
        dish += materials[i];
      } else {
    
    
        dish += materials[i] + "+";
      }

    }
    return dish;
  }

  @Override
  public void serveDishes(String dish) {
    
    
    System.out.println(dish);
  }
}

案例二(银行业务办理)

将银行业务办理的标准流程抽取出来:取号、办理实际业务、服务评价,接着将这个标准流程抽象成一个具体方法(即定义一个具体方法来描述这个业务流程),取名为“办理业务”。而流程中的取号和服务评价环节的内容是固定不变的,所以可以将这两个步骤抽象成对应的两个具体方法(即定义两个具体方法来分别描述流程中的取号和服务评价步骤)。办理实际业务这个环节的具体内容则因不同的银行业务而不同,所以可以将这个环节抽象成一个抽象方法(即声明一个抽象方法来描述业务办理这个步骤),这样就可以让不同的业务类根据实际的业务需求去实现这个抽象方法了。

到此就很明显了,我们需要将银行办理业务这件事抽象成一个抽象类(即定义一个抽象类来描述银行办理业务这件事情),这个抽象类中含有一个抽象方法(办理实际业务),将取号和服务评价分别抽象成两个具体方法,将银行办理业务的标准流程抽象成一个具体的方法,这个方法中去调用标准流程对应的三个方法。最后将不同的银行业务抽象成具体类(即定义不同的具体类来描述不同的银行业务),具体类再去继承抽象类,这样具体类就可以根据实际的业务需要去实现抽象类中的抽象方法,并且也拥有了“办理业务”的具体方法。

抽象类 BankBusinessHandler 的代码:

package priv.lwx.design.template;


import javax.validation.constraints.NotNull;
import java.util.Random;

/**
 * 银行业务办理类
 *
 * @author liaowenxiong
 * @date 2022/6/6 14:33
 */

public abstract class BankBusinessHandler {
    
    

  /**
   * 办理银行业务
   *
   * @param
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 15:10
   */
  public void handleBusiness(Object obj) {
    
    
    // 取号
    Integer number = this.takeNumber();
    // Integer number = null;
    // 办理实际业务
    Integer result = this.handle(number,obj);
    // 评价业务
    this.appraise(result);

  }

  /**
   * 取号
   *
   * @param
   * @return Integer
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 14:54
   */
  public Integer takeNumber() {
    
    
    Random r = new Random();
    // 在[0,999]区间内获取一个随机号
    Integer number = r.nextInt(1000);
    return number;
  }

  /**
   * 评价服务
   *
   * @param result
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 14:56
   */
  public String appraise(Integer result) {
    
    
    String comment = "";
    if (result == 0) {
    
    
      comment = "满意";
    } else if (result == 1) {
    
    
      comment = "一般";
    } else {
    
    
      comment = "糟糕";
    }
    System.out.println(comment);
    return comment;
  }

  /**
   * 声明办理实际业务的抽象方法
   *
   * @param
   * @return
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 14:58
   */
  public abstract Integer handle(@NotNull Integer number, @NotNull Object obj);
}

存款实现类 Deposit 的代码:

package priv.lwx.design.service;

import priv.lwx.design.template.BankBusinessHandler;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

/**
 * 存款业务
 *
 * @author liaowenxiong
 * @date 2022/6/6 15:41
 */

public class Deposit extends BankBusinessHandler {
    
    

  @Override
  public Integer handle(@NotNull Integer number, @NotNull Object obj) {
    
    
    System.out.println("叫号:" + number);
    BigDecimal amountBD = (BigDecimal) obj;
    double amount = amountBD.doubleValue();
    Integer result = 2;
    if (amount >= 1000) {
    
    
      System.out.println("-- 办理存款业务 --");
      System.out.println("存款金额:" + amount);
      result = 0;
    } else if (amount < 1000 && amount >= 500) {
    
    
      System.out.println("-- 办理存款业务 --");
      System.out.println("存款金额:" + amount);
      result = 1;
    } else {
    
    
      System.out.println("-- 办理存款业务 --");
      System.out.println("存款金额:" + amount);
      result = 2;
    }
    return result;
  }
}

取款实现类 Withdraw 的代码:

package priv.lwx.design.service;

import com.sun.istack.internal.NotNull;
import priv.lwx.design.template.BankBusinessHandler;

/**
 * 取款业务
 *
 * @author liaowenxiong
 * @date 2022/6/6 15:17
 */

public class Withdraw extends BankBusinessHandler {
    
    
  /**
   * 办理实际业务
   *
   * @param number
   * @param obj
   * @return Integer
   * @throws
   * @author liaowenxiong
   * @date 2022/6/6 15:40
   */
  @Override
  public Integer handle(@NotNull Integer number, @NotNull Object obj) {
    
    
    Integer result = 2;
    if ((Integer) obj >= 1000) {
    
    
      System.out.println("-- 办理取款业务 --");
      System.out.println("取款金额:" + (Integer) obj);
      result = 0;
    } else if ((Integer) obj < 1000 && (Integer) obj >= 500) {
    
    
      System.out.println("-- 办理取款业务 --");
      System.out.println("取款金额:" + (Integer) obj);
      result = 1;
    } else {
    
    
      System.out.println("-- 办理取款业务 --");
      System.out.println("取款金额:" + (Integer) obj);
      result = 2;
    }
    return result;
  }
}

猜你喜欢

转载自blog.csdn.net/liaowenxiong/article/details/125131353