您的位置:首页 > 编程语言 > Java开发

java设计模式之模板方法模式

2017-04-05 22:24 225 查看
一、模板方法模式简介

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

这个模式是用来创建一个算法的模板。什么是模板?模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。

模板方法模式涉及到两个角色:

抽象模板(Abstract Template)角色:

--定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。

--定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。

具体模板(Concrete Template)角色:

--实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。

--每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

模板模式使用场景:

1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

2、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。

3、控制子类的扩展。

二、示例演示

1、业务需求

这里来利用模板方法模式来封装“冲咖啡”和“泡茶”两个具体的业务。

首先来看泡咖啡的流程:

把水煮沸-->用沸水冲泡咖啡-->把咖啡倒进杯子-->加糖和牛奶

再来看泡茶的流程:

把水煮沸-->用沸水冲泡茶叶-->把茶倒进杯子-->加柠檬

通过分析这两个业务流程,我们可以看出泡咖啡和泡茶能共用一个相同的泡法(算法):

把水煮沸-->用沸水冲泡-->倒入杯子中-->加入调料。

2、定义流程的抽象类,该抽象类提供了冲泡咖啡或者茶的具体流程,并且实现了逻辑步骤,煮沸水和倒入杯子中。将用沸水冲泡和加入调料交由具体的子类(咖啡、茶)来实现。
public abstract class CaffeineBeverage {
// 现在,用同一个prepareRecipe()方法来处理茶和咖啡。
// prepareRecipe()方法被声明为final,因为我们不希望子类覆盖这个方法
// 我们将第2步和第4步泛化成为brew()和addCondiments()
final void prepareRecipe() {
boilWater();//烧水
brew();//冲泡
pourInCup();//倒入杯中
addCondiments();//加入调料
}

// 因为咖啡和茶处理这些方法的做法不同,所以这两个方法必须被声明为抽象,
// 剩余的东西留给子类去操心
abstract void addCondiments();
abstract void brew();

public void boilWater() {//冲咖啡和泡茶的这个方法相同
System.out.println("Boiling water");
}

public void pourInCup() {
System.out.println("Pouring into cup");
}
}
3、定义咖啡和茶的具体流程实现,这两个类现在都是依赖超类来处理冲泡法,所以只需要自行处理冲泡和添加调料部分
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");
}
}
4、编写测试类
public class Test {
public static void main(String[] args) {
//冲咖啡
Coffee coffee=new Conffee();
coffee.prepareRecipe();
System.out.println("冲咖啡完毕");
//泡茶
Tea tea = new Tea();
tea.prepareRecipe();
System.out.println("泡茶完毕");

}
}
测试结果:
Boiling water
Dripping coffee through filter
Pouring into cup
Adding Sugar and Milk
冲咖啡完毕
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon
泡茶完毕


三、对模板方法进行挂钩

如果某些客户并不喜欢加入调料,而喜欢原生态的,但是我们的算法总是会给客户加入调料,怎么解决? 

遇到这个问题我们可以使用钩子。所谓钩子就是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在可以使子类能够对算法的不同点进行挂钩,即让子类能够对模板方法中某些即将发生变化的步骤做出相应的反应。当然要不要挂钩,由子类决定。

所以对于上面的要求,我们可以做出如下修改:

1、流程的抽象类
public abstract class CaffeineBeverageWithHook {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
// 我们加上了一个小小的条件语句,而该条件是否成立,
// 是由一个具体方法customerWantsCondiments()决定的。
// 如果顾客“想要”调料,只有这时我们才调用addCondiments()。
if (customerWantsCondiments()) {
addCondiments();
}
}

abstract void addCondiments();
abstract void brew();

public void boilWater() {
System.out.println("Boiling water");
}

public void pourInCup() {
System.out.println("Pouring into cup");
}

// 我们在这里定义了一个方法,(通常)是空的缺省实现。这个方法只会返回true,不做别的事。
// 这就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做。
boolean customerWantsCondiments() {
return true;
}
}
2、泡咖啡的流程实现
public class Coffee extends CaffeineBeverageWithHook {
public boolean isAddCondiments=true;//默认加入调料
@Override
void brew() {
System.out.println("Dripping coffee through filter");
}

@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
boolean customerWantsCondiments() {
return isAddCondiments;
}
public void setIsAddCondiments(boolean isAddCondiments){
this.isAddCondiments=isAddCondiments;
}
}
泡茶的实现与之类似,这里不做详述。

四、总结

模板方法模式的优缺点

优点:

1、模板方法模式在定义了一组算法,将具体的实现交由子类负责。

2、模板方法模式是一种代码复用的基本技术。

3、模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。

缺点:

每一个不同的实现都需要一个子类来实现,导致类的个数增加,是的系统更加庞大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: