1.什么是模板方法模式?
模板方法模式抽象出一个模板,该模板中具有完成某项功能的算法骨架,模板中只对一些固定不变的算法做实现,其余的算法实现延迟到子类中去实现.
举个生活中的例子,比如去银行办业务,一般需要以下几个步骤:
①取号;②填写办理项目单据;③等待叫号;④窗口办理业务.
其中①,③,④这三个步骤是固定不变的,基本上99%的人去银行办业务都会经历这三个步骤,而且顺序不变,只有步骤②是可变的,每个人所填的单据都是不一样的,所以这一项内容交给客户去做了,其它三项由银行完成,这样一来既方便了客户,也节省了时间,提高了效率.在开发中亦是如此,可以把实现某个功能的固定步骤抽取出来作为一个模板,然后把模板中固定不变的方法在模板中实现(父类),把一些需要变动的方法在模板中抽象,交给继承它的子类去实现,这种设计模式就称之为模板方法模式.
2.为什么要用模板方法模式?
①可以更好的封装代码,体现java语言面向对象的特性.
②提高代码的复用性,将重复不变的部分抽出去让父类实现,子类继承就能直接用,给猿类节省陪女朋友的时间…
③屏蔽细节,子类只需要重点部分差异代码的实现,无需再关心父类中已经实现的代码细节,毕竟专注才能更高效嘛.
④易于维护,较高的复用性使得业务代码重复更少,代码量减少很多,使得代码维护起来更加容易.
3.适用场景
业务中有些功能的实现步骤是比较清晰和固定的,算法或操作中遵循的逻辑相似.
4.实现
我想用代码来实现下”正常”人的一天,总结一下,人的一天不就是起床,吃饭,睡觉,干一些事吗,说简单一点,就像小沈阳当年说的,眼睛一闭一睁,一天没了…观察了很多人的一天,我得出一个模板:
起床,吃饭,做一些事,睡觉.
其中起床,吃饭,睡觉是不变的,可以在父类中实现,做一些事的话每个人都不一样,不同的人可能会把时间拿来打游戏,工作,学习,娱乐…所以这个步骤我延迟到子类中去实现,由于人太多了,我代码里就尽量简单,举个A人和B人的例子就行了,重在理解…
先创建模板,为了防止子类修改父类中固定不变的方法,父类中固定不变的方法需要加final关键字,同时需要加上private关键字对子类隐藏方法.
/**
* 模板方法模式-模板:一个人的一天
*/
public abstract class LifeTemplate {
public void Life() {
//起床
getUp();
//吃饭
eat();
//做一些事
dowork();
//睡觉
sleep();
}
private final void getUp() {
System.out.println("起床了...");
}
private final void eat() {
System.out.println("大猪蹄子真好吃...");
}
protected abstract void dowork();
private final void sleep() {
System.out.println("我要睡个香香觉...");
}
}
再分别创建A人和B人,继承模板方法并实现抽象方法:
public class PersonA extends LifeTemplate {
@Override
protected void dowork() {
System.out.println("我是A,我在努力工作赚老婆本...");
}
}
public class PersonB extends LifeTemplate{
@Override
protected void dowork() {
System.out.println("我是B,我很牛逼,不用工作,我在打游戏...");
}
}
测试一下:
/**
* 测试类
*/
public class LifeTemplateTest {
public static void main(String[] args) {
LifeTemplate A = new PersonA();
LifeTemplate B = new PersonB();
System.out.println("有请A表演他的一天>>>");
A.Life();
System.out.println("*****************************");
System.out.println("有请B表演他的一天>>>");
B.Life();
}
}
结果如图:
嗯,没毛病,模板方法就是好,但是有一天,A经历了双11之后,像我一样不仅吃不起大猪蹄子,连土都吃不起了,怎么办?绝食一天吧…
这个时候由于模板中已经提供了该方法的实现,而且加了fInal,A无可奈何了吧… 这时就需要引出接下来要讲的钩子Hook
可以在模板方法中加上钩子,让子类决定是否调用模板中提供的方法,这样就非常灵活了,你也可以把钩子当做是父类中对应方法的开关,由子类选择开启或关闭.
于是我们可以给模板方法中加上一条if语句和一个方法,默认返回true,也就是默认要吃早饭,该方法不加final,子类可以选择覆盖/不覆盖,修改后的模板方法代码如下:
/**
* 模板方法模式-模板:一个人的一天
*/
public abstract class LifeTemplate {
public void Life() {
//起床
getUp();
//吃饭
if (isEat()) {
eat();
}
//做一些事
dowork();
//睡觉
sleep();
}
/**
* 钩子
*/
protected boolean isEat() {
return true;
}
private final void getUp() {
System.out.println("起床了...");
}
private final void eat() {
System.out.println("大猪蹄子真好吃...");
}
protected abstract void dowork();
private final void sleep() {
System.out.println("我要睡个香香觉...");
}
}
然后A类中覆盖该钩子,返回false:
public class PersonA extends LifeTemplate {
@Override
protected void dowork() {
System.out.println("我是A,我在努力工作赚老婆本...");
}
@Override
protected boolean isEat() {
return false;
}
}
B类不需要变动,因为B是富二代,不需要吃土…
再来测试一下:
有了钩子之后,是不是很方便呢?如果哪天B想打游戏打通宵也没问题,加个钩子就行了,哪天再来个C,D,E…分别写个dowork的实现就行了,其他代码直接复用,写代码变得如此简单,终于有时间陪家人了,这就是设计模式的魅力,也难怪大公司都会要求应聘者掌握一些设计模式,确实可以让代码质量提高很多…
5.缺点
凡事有利就有弊,设计模式也是如此,由于java是单继承的,如果你采取了模板方法模式,子类就无法再继承其它的类了,所以在使用过程中需要注意这一点,但无论如何也无法掩盖它的优点,在实际开发中还是很推荐使用的,而且该设计模式在三大运营商的日志模块的业务代码里有大量运用,经得起实际考验,所以放心去用吧.