设计模式——模板方法模式
Posted 0x3f3f3f3f
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式——模板方法模式相关的知识,希望对你有一定的参考价值。
模板方法模式定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
例子
举一个生活中的场景:泡咖啡和泡茶。
泡咖啡的步骤为:
- 把水煮沸
- 用沸水冲泡咖啡
- 把咖啡倒进杯子
- 加糖和牛奶
泡茶的步骤为:
- 把水煮沸
- 用沸水浸泡茶叶
- 把茶倒进杯子
- 加柠檬
我们用java类来实现这两个过程,代码如下
//咖啡类
public class Coffee
void prepareRecipe()
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
private void addSugarAndMilk()
System.out.println("Adding Sugar and Milk");
private void pourInCup()
System.out.println("Pouring into cup");
private void brewCoffeeGrinds()
System.out.println("Dripping coffee through filter");
private void boilWater()
System.out.println("Boiliing water");
//茶类
public class Tea
void prepareRecipe()
boilWater();
steepTeaBag();
pourInCup();
addLemon();
private void addLemon()
System.out.println("Add Lemon");
private void pourInCup()
System.out.println("Pouring into cup");
private void steepTeaBag()
System.out.println("Steeping the tea");
private void boilWater()
System.out.println("Boiling water");
在这两个类中,我们可以看到,$boilWater()$ 方法和 $pourIncup()$ 方法是茶和咖啡类共有的,而剩下的两个方法是茶类和咖啡类专有的。因此我们可以设计一下,把共同的部分抽取出来,放到一个基类中。
而不同的部分虽然不能被抽取出来,但是他们本质上其实是一样的,只是应用到了不同的饮料上。这样,我们就可以从这两个子类中再次抽象一下,把冲泡方法和加调料方法抽象出来。
父类代码如下
public abstract class CaffeineBeverage
final void prepareRecipe()
boilWater();
brew();
pourInCup();
addCondiments();
abstract void brew();
abstract void addCondiments();
void boilWater()
System.out.println("Boiling water");
void pourInCup()
System.out.println("Pouring into cup");
这里咖啡因饮料是一个抽象类。
我们用同一个$prepareRecipe()$方法来处理茶和咖啡,$prepareRecipe()$被声明为$final$,因为我们不希望子类覆盖这个方法。
因为咖啡和茶的处理方法不同,因此我们把这两个方法声明为抽象方法,剩余的东西留给子类去实现。
继承咖啡因饮料类来实现咖啡和茶类
//咖啡类
public class Coffee extends CaffeineBeverage
@Override
void brew()
System.out.println("Dripping Coffee through filter");
@Override
void addCondiments()
System.out.println("Adding Sugar and Milk");
//茶类
public class Tea extends CaffeineBeverage
@Override
void brew()
System.out.println("Steeping the tea");
@Override
void addCondiments()
System.out.println("Adding Lemon");
上述就是一个模板方法模式的经典案例,而其中的$prepareRecipe()$就是一个模板方法。在这个模板中,算法内的每一个步骤都被一个方法代表了,某些方法是由这个类来实现的,而某些方法是由子类实现的,子类实现的方法必须在父类中声明为抽象方法。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
对模板方法进行挂钩
钩子是一种被声明在抽象类中的方法,但是只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
使用钩子的例子
public abstract class CaffeineBeverageWithHook
void prepareRecipe()
boilder();
brew();
pourInCup();
if (customerWantsCondiments())
addCondiments();
abstract void brew();
abstract void addCondiments();
void boilder()
System.out.println("Boilding water");
void pourInCup()
System.out.println("Pouring into cup");
boolean customerWantsCondiments()
return true;
在上述抽象类中,我们加上了一个条件语句,而该条件是否成立是由一个具体方法$customerWantsCondiments()$决定的,如果顾客想要加调料,这是我们才调用$addCondiments()$
在父类中,我们定义出这个方法,通常是空的缺省实现。这个方法只会返回$true$,不做别的事。这就是一个钩子,子类可以覆盖这个方法,但不一定要这么做。
而为了使用钩子,我们就需要在子类中覆盖它。具体实现由业务逻辑决定。在这里,钩子控制了饮料中是否加调料,因此,实现逻辑可以是简单的询问顾客的输入做判断即可。
实现
public class CoffeeWithHook extends CaffeineBeverageWithHook
@Override
public void brew()
System.out.println("Dripping Coffee through filter");
@Override
public void addCondiments()
System.out.println("Adding Sugar and Milk");
@Override
public boolean customerWantsCondiments()
String answer = getUserInput();
if (answer.toLowerCase().startsWith("y"))
return true;
else
return false;
private String getUserInput()
Scanner sc = new Scanner(System.in);
System.out.println("Would you like milk and sugar with your coffee (y / n) ?");
String answer = sc.nextLine();
return answer;
上述咖啡类就继承了带有钩子的父类,并且重写了钩子方法,对用户的输入做了判断,已达到钩子的效果。
写一个测试类来测试一下
public class CoffeeTest
public static void main(String[] args)
CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.prepareRecipe();
执行结果
以上是关于设计模式——模板方法模式的主要内容,如果未能解决你的问题,请参考以下文章