今天学习模板方法模式,模板方法模式很简单,通俗的来讲,模板方法就是将一系列的顺序排好的,但是具体实现可能不一样的一种模式。这说起来有点绕口,也不好理解,我们举个例子先。
每个人都去过超市买东西,那么,买东西的这个操作有没有什么规律呢?当然有,我想,所有的人都是三个步骤——进入超市,拿东西,付钱。
好,那么我们就以上边的这个例子,根据实际代码,来理解一下什么是模板方法
首先,根据那三个具体步骤,我们新建一个抽象类(注意加粗的部分),至于为什么是抽象类,我会稍后解释。在写代码之前,有一个概念要和大家说一下,就是模板方法里边,分为三种不同的方法,分别是:抽象方法,具体方法,钩子方法,这三个方法会在代码注释中予以解释
Shopping类
package com.templatemethod.template;
/**
* 模板方法的抽象类,用于封装固定的步骤或操作
* @author ZHENGWEI
* @date Jul 24, 2015
*/
public abstract class Shopping {
/**
* 此处是完成购物的操作步骤
* 我们每个人购物的步骤都是一样的
* 进入超市,那东西,付钱
*/
public void accomplishShopping(){
goToMarket();
double totalMoney = bySomething();
doPay(totalMoney);
}
/**
* 这是一个具体方法
* 每个人购物都要先进入超市,这个步骤不管是谁购物,是一定要执行的
*/
private final void goToMarket(){
System.out.println("进入了XXXX超市");
}
/**
* 这是一个抽象方法
* 进入超市之后,就要买东西,但是买什么我们就不确定了
* 所以子类继承的时候,一定要重写这个方法,来告知这个人具体买了什么东西
*/
protected abstract double bySomething();
/**
* 这是一个钩子方法
* 钩子方法的意思是在子类中是否重写不做强制要求,如果子类没有重写,那么就用此模板的实现方法
* 如果子类重写了,那么久选择使用子类重写之后的方法
* 在结账的时候,我们通常会选择现金支付,但是也有选择刷卡支付的
* 所以默认为现金支付,如果子类没有重写此方法,则默认为现金支付
* @param totalMoney
*/
protected void doPay(double totalMoney){
System.out.println("这些商品价格总共为:"+totalMoney+"元");
System.out.println("我们应该收您现金"+totalMoney+"元");
}
}
之后再写一个继承这个抽象类的类ShoppingByMarket
package com.templatemethod.realize;
import com.templatemethod.template.Shopping;
public class ShoppingByMarket extends Shopping{
@Override
protected double bySomething() {
System.out.println("您买了一袋方便面");
return 3.5;
}
}
这个类只重写了那个必须要重写的类,其余的全部用默认值
测试类
package com.templatemethod.main;
import com.templatemethod.realize.ShoppingByMarket;
import com.templatemethod.template.Shopping;
public class TestTemplate {
public static void main(String[] args) {
Shopping shopping = new ShoppingByMarket();
shopping.accomplishShopping();
}
}
测试结果
看完这个例子之后,不知道有没有对这个模板方法有一个大概的了解呢?我们其实还可以将抽象类里的那个钩子方法重写,比如选择使用银行卡的方式,等等。
但是不管如何改变,买东西的那三个步骤是绝对不会变的,这才是根源所在。在实际的项目中,将固定的步骤封装起来,使用模板方法的模式,将会事半功倍。
模板方法原则上就是,不变的内容写到具体方法里,(权限为private),一定有变化的写成抽象方法,以便在子类重写,可变可不变的写成钩子方法。