模板方法模式是一种很常见也很有用的设计模式,在android 架构中和一些开源框架中也是出现频率很高的。 如果你只想掌握一种设计模式,那强烈推荐模板方法模式。
那么什么是模板方法模式呢。我们先来看一下定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。看完定义我们再来看一下模板方法中的方法,模板方法中方法可以分为两大类:模板方法和基本方法
一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。
基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。
抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。
看完之前的概念我们还是不太清楚我们还是引用个例子说明一下,假如我们是一家快餐店制作汉堡,那我们是不是要有个制作流程
1.准备材料(制作肉饼,烤面包等等)
2.放生菜
3.放肉
4.打酱
5.包装
此时一位顾客点了一份香辣汉堡和一个不辣的汉堡,那么我们开始只做汉堡吧。
首先制作汉堡的抽象模型。
public abstract class hamburgerModel {
/**
* 准备工作
*/
public abstract void prepare();
/**
* 加入生菜
*/
public abstract void addGreens();
/**
* 加入肉
*/
public abstract void addMeat();
/**
* 放入酱
*/
public abstract void addSalad();
/**
* 包装
*/
public abstract void pack();
/**
* 制作流程
*/
public abstract void course();
}
制作香辣汉堡
public class hamburger1 extends hamburgerModel{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备过程");
}
@Override
public void addGreens() {
// TODO Auto-generated method stub
System.out.println("放菜");
}
@Override
public void addMeat() {
// TODO Auto-generated method stub
System.out.println("放肉");
}
@Override
public void addSalad() {
// TODO Auto-generated method stub
System.out.println("放沙拉酱");
}
@Override
public void pack() {
// TODO Auto-generated method stub
System.out.println("包装");
}
@Override
public void course() {
// TODO Auto-generated method stub
prepare();
addGreens();
addMeat();
addSalad();
pack();
}
}
制作不辣汉堡
public class hamburger2 extends hamburgerModel{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备过程");
}
@Override
public void addGreens() {
// TODO Auto-generated method stub
System.out.println("放菜");
}
@Override
public void addMeat() {
// TODO Auto-generated method stub
System.out.println("放肉");
}
@Override
public void addSalad() {
// TODO Auto-generated method stub
System.out.println("放沙拉酱");
}
@Override
public void pack() {
// TODO Auto-generated method stub
System.out.println("包装");
}
@Override
public void course() {
// TODO Auto-generated method stub
prepare();
addGreens();
addMeat();
addSalad();
pack();
}
}
好了,到这里已经发现问题了,在两个实现类里都实现了course()方法,显然这里是可以改变逻辑的与概念不符,并且这样做基类也失去了控制,这是我们不想要的。
那么我们来修改下:
模板基类
public abstract class hamburgerModel {
/**
* 准备工作
*/
public abstract void prepare();
/**
* 加入生菜
*/
public abstract void addGreens();
/**
* 加入肉
*/
public abstract void addMeat();
/**
* 放入酱
*/
public abstract void addSalad();
/**
* 包装
*/
public abstract void pack();
/**
* 制作流程
*/
public final void course(){
prepare();
addGreens();
addMeat();
addSalad();
pack();
}
}
制作香辣汉堡
public class hamburger1 extends hamburgerModel{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备过程");
}
@Override
public void addGreens() {
// TODO Auto-generated method stub
System.out.println("放菜");
}
@Override
public void addMeat() {
// TODO Auto-generated method stub
System.out.println("放肉");
}
@Override
public void addSalad() {
// TODO Auto-generated method stub
System.out.println("放沙拉酱");
}
@Override
public void pack() {
// TODO Auto-generated method stub
System.out.println("包装");
}
}
制作不辣汉堡
public class hamburger2 extends hamburgerModel{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备过程");
}
@Override
public void addGreens() {
// TODO Auto-generated method stub
System.out.println("放菜");
}
@Override
public void addMeat() {
// TODO Auto-generated method stub
System.out.println("放肉");
}
@Override
public void addSalad() {
// TODO Auto-generated method stub
System.out.println("放沙拉酱");
}
@Override
public void pack() {
// TODO Auto-generated method stub
System.out.println("包装");
}
}
这样看起来是不是比第一个要好,我们把流程方法写成具体方法放在抽象模板类中,这样我们定义了一个算法流程,子类不可以修改逻辑,逻辑由父类负责,子类只作具体 的实现。这样做责任更加清楚,减少慵虞代码。那么其实这样还不够完美,此时又来了一个顾客要一个香辣汉堡要求不放酱。那么我们现有的模板是不满足这个要求的,我们还要进行优化。
public abstract class hamburgerModel {
/**
* 准备工作
*/
public abstract void prepare();
/**
* 加入生菜
*/
public abstract void addGreens();
/**
* 加入肉
*/
public abstract void addMeat();
/**
* 放入酱
*/
public abstract void addSalad();
/**
* 包装
*/
public abstract void pack();
/**
* 制作流程
*/
public final void course(){
prepare();
addGreens();
addMeat();
if(this.isAdd()){
addSalad();
}
pack();
}
protected boolean isAdd(){
return true;
}
}
public class hamburger1 extends hamburgerModel{
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备过程");
}
@Override
public void addGreens() {
// TODO Auto-generated method stub
System.out.println("放菜");
}
@Override
public void addMeat() {
// TODO Auto-generated method stub
System.out.println("放肉");
}
@Override
public void addSalad() {
// TODO Auto-generated method stub
System.out.println("放沙拉酱");
}
@Override
public void pack() {
// TODO Auto-generated method stub
System.out.println("包装");
}
@Override
public void isAdd(){
//这里在子类可以控制是否放酱
return ture;或者return false ;
}
}
这里我们增加了一个isAdd()方法影响模板方法的执行,这个方法就是钩子方法。有了钩子方法模板方法模式才算完美,
大家可以想一下,不是每个实现类都需要所有流程的,那么钩子方法就帮我们解决了这个问题,由子类返回值决定是否要执行。
到这里我们再回过头来对应概念去理解一下模板方法模式,首先有一个模板基类,这个类可以是抽象,也可以不是抽象。基类里有一个控制总流程的方法,这个方法就是模板方法,在我们的例子中 course()就是模板方法,也就是算法骨架,他将逻辑流程放在这个方法里,这个方法一般是final修饰防止窜改。像addGreens(),addGreens()等这些抽象方法是让基类去实现的,在不同的子类去扩展,这是不是就是概念中的将一些步骤延迟到子类中 。最后就是 isAdd()这个方法了,只是一个钩子方法,他的作用是完成一些特定步骤也就是概念中的模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。当然普通方法也可以是具体方法,根据项目需要而定,这里建议去学习下view,Activity 源码。