概念
模板模式 | 菜鸟教程
模板方法设计模式是行为型设计模式中的一种,用在一个功能的完成需要经过一系列步骤,
这些步骤是固定的,但是中间某些步骤具体行为是待定的,在不同的场景中行为不同,
此时就可以考虑使用模板方法设计模式来完成,不同的场景对应不同的子类实现。
模板方法包含如下的角色:
抽象类/抽象模板(Abstract Class):负责给出一个算法的轮廓,由一个模板方法和若干个基本方法构成,
这些方法定义如下
1:模板方法(该方法也正是模式的核心),定义算法的框架,按照某种顺序调用其中的基本方法。
2:基本方法,可以包含如下的几种类型
抽象方法:由具体的子类实现,作为定制行为,因为是抽象方法所以子类必须实现。
具体方法:在抽象类中已经提供了具体的实现,子类可以继承或者重写(按照里氏替换原则,
最好不要重写)。
钩子方法:在抽象类中已经提供了实现(一般是空实现),类似于抽象方法,但是并非强制
子类实现,因为已经提供了默认实现,可以在需要进行能力扩展时使用。
具体子类/具体实现(Concrete Class):必须实现抽象模板中的抽象方法,以及选择性的重载钩子方法。
定义一个模板接口
public interface Cookingtemplate {
void buySth();
void washFood();
void cook();
void eat();
String make();
}
实现模板接口
@Slf4j
@Service
public class CookingFruitImpl implements Cookingtemplate {
@Override
public String make() {
buySth();
washFood();
cook();
eat();
return "水果大餐";
}
@Override
public void buySth() {
log.info("买些苹果,芒果,香蕉,水蜜桃");
}
@Override
public void washFood() {
log.info("把水果洗干净");
}
@Override
public void cook() {
log.info("放点沙拉酱做水果沙拉");
}
@Override
public void eat() {
log.info("享用水果沙拉");
}
}
@Slf4j
@Service
public class CookingVegetableImpl implements Cookingtemplate {
@Override
public String make() {
buySth();
washFood();
cook();
eat();
return "蔬菜大餐";
}
@Override
public void buySth() {
log.info("买些菠菜,土豆,西红柿");
}
@Override
public void washFood() {
log.info("把蔬菜洗干净");
}
@Override
public void cook() {
log.info("做个西红柿炒土豆丝,炒个菠菜");
}
@Override
public void eat() {
log.info("享用小炒");
}
}
定义枚举类将食物类型与烹饪模板实现类关联起来
/**
* 做饭枚举
*/
public enum CookingEnum {
/**
* 蔬菜实现类枚举
*/
VEGETABLE("vegetable","cookingVegetableImpl"),
/**
* 水果实现类枚举
*/
FRUIT("fruit","cookingFruitImpl");
private final String key;
private final String value;
CookingEnum(String key, String value) {
this.key=key;
this.value=value;
}
/**
* 获取key
* @return
*/
public String getKey(){
return key;
}
/**
* 获取value
* @return
*/
public String getValue(){
return value;
}
}
控制器调用
/**
* 模板方法设计模式+策略模式
* 根据食物类型选择不同的工序出餐享用
* @param foodType
* @return
*/
@GetMapping("/cook")
public ResponseModel cook(String foodType) {
String serviceName="";
if (CookingEnum.FRUIT.getKey().equals(foodType)){
serviceName=CookingEnum.FRUIT.getValue();
}else if (CookingEnum.VEGETABLE.getKey().equals(foodType)){
serviceName=CookingEnum.VEGETABLE.getValue();
}else {
log.error("超标了,做不出来这种饭");
return new ResponseModel("超标了,做不出来这种饭", 500, null);
}
Cookingtemplate bean = applicationContext.getBean(serviceName, Cookingtemplate.class);
String make = bean.make();
return new ResponseModel("做好了", 200, make);
}
测试案例
2022-06-12 23:41:04.591 INFO 21536 --- [nio-8081-exec-6] c.k.s.d.t.impl.CookingVegetableImpl : 买些菠菜,土豆,西红柿
2022-06-12 23:41:04.591 INFO 21536 --- [nio-8081-exec-6] c.k.s.d.t.impl.CookingVegetableImpl : 把蔬菜洗干净
2022-06-12 23:41:04.591 INFO 21536 --- [nio-8081-exec-6] c.k.s.d.t.impl.CookingVegetableImpl : 做个西红柿炒土豆丝,炒个菠菜
2022-06-12 23:41:04.591 INFO 21536 --- [nio-8081-exec-6] c.k.s.d.t.impl.CookingVegetableImpl : 享用小炒
2022-06-12 23:42:13.353 INFO 21536 --- [nio-8081-exec-9] c.k.s.d.template.impl.CookingFruitImpl : 买些苹果,芒果,香蕉,水蜜桃
2022-06-12 23:42:13.353 INFO 21536 --- [nio-8081-exec-9] c.k.s.d.template.impl.CookingFruitImpl : 把水果洗干净
2022-06-12 23:42:13.353 INFO 21536 --- [nio-8081-exec-9] c.k.s.d.template.impl.CookingFruitImpl : 放点沙拉酱做水果沙拉
2022-06-12 23:42:13.353 INFO 21536 --- [nio-8081-exec-9] c.k.s.d.template.impl.CookingFruitImpl : 享用水果沙拉