
模板模式 :处理某类事情的步骤有些是固定的,有些是会发生变化的,那么这时候我们可以为这类事情提供一个模板代码,从而提高效率。
办理银行业务:
准备一个笼统类,将部分逻辑以具体方法的形式实现,而后公告少量笼统方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模版方法。
案例一:编写一个计算程序运行时间的模板。
笼统基类:
abstract class MyRuntime{ public final void getTime(){ long startTime = System.currentTimeMillis(); //记录开始的时间 code(); long endTime = System.currentTimeMillis(); //记录结束的时间. System.out.println("运行时间 :"+ (endTime-startTime)); } public abstract void code();}具体子类:
class Demo extends MyRuntime{ public static void main(String[] args) { Demo d = new Demo(); d.getTime(); } //code方法内部就写要计算运行时间的代码; public void code(){ int i = 0; while(i<100){ System.out.println("i="+i); i++; } }}案例二:饮料的制法:
把水煮沸(boilWater)冲饮料(brew)把饮料倒进杯子(pourInCup)加调味料(addCondiments)笼统基类:Drinks
package com.hcx.pattern.template;/** * 笼统基类,为所有子类提供一个算法框架 * 饮料 * @author HCX * */public abstract class Drinks { /** * 使用final修饰,防止子类改变模版方法 * 制备饮料的模版方法 * 封装了所有子类共同遵循的算法框架 */ public final void prepareDrinksTemplate(){ //步骤一:把水煮沸 boilWater(); //步骤二:冲饮料 brew(); //步骤三:把饮料倒进杯子 pourInCup(); //步骤四:加调味料 addCondiments(); } /** * 基本方法:把水煮沸 * 对所有子类,是一个共同的行为,不需要向子类开放;将变化的东西放在高层代码中。 */ private void boilWater() { System.out.println("把水煮沸"); } /** * 基本方法:将饮料倒入杯中 */ private void pourInCup() { System.out.println("将饮料倒入杯中"); } /** * 不同的情况,具体的实现不同,设计为笼统方法,需要在子类中可见,以便子类复写,提供具体的实现。 * 笼统的基本方法:加入调料 */ protected abstract void addCondiments(); /** * 笼统的基本方法:泡饮料 */ protected abstract void brew();}具体子类:Coffee
package com.hcx.pattern.template;/** * 具体子类,提供了咖啡制备的具体实现 * @author HCX * */public class Coffee extends Drinks { @Override protected void brew() { System.out.println("用沸水冲泡咖啡"); } @Override protected void addCondiments() { System.out.println("加入糖和牛奶"); }}具体子类:OrangeJuice
package com.hcx.pattern.template;/** * 具体子类,提供了橙汁的具体实现 * @author HCX * */public class OrangeJuice extends Drinks{ @Override protected void brew() { System.out.println("准备橙子和榨汁机,把橙子丢入机器中榨汁"); } @Override protected void addCondiments() { System.out.println("加入糖浆"); }}测试:
package com.hcx.pattern.template;public class DrinksTest { public static void main(String[] args) { System.out.println("咖啡制备中"); Drinks drinks = new Coffee(); drinks.prepareDrinksTemplate(); System.out.println("咖啡好了"); System.out.println("*************************************"); System.out.println("橙汁制备中"); Drinks drinks2 = new OrangeJuice(); drinks2.prepareDrinksTemplate(); System.out.println("橙汁好了"); }}结果:
咖啡制备中把水煮沸用沸水冲泡咖啡将饮料倒入杯中加入糖和牛奶咖啡好了*************************************橙汁制备中把水煮沸准备榨汁机和榨汁机,把橙子丢入机器中榨汁将饮料倒入杯中加入糖浆橙汁好了使用钩子方法使代码更灵活:
在制备橙汁时,不想加入糖浆;
修改Drinks类,在加入调味料的步骤进行判断,编写钩子函数:
package com.hcx.pattern.template;/** * 笼统基类,为所有子类提供一个算法框架 * 饮料 * @author HCX * */public abstract class Drinks { /** * 使用final修饰,防止子类改变模版方法 * 制备饮料的模版方法 * 封装了所有子类共同遵循的算法框架 */ public final void prepareDrinksTemplate(){ //步骤一:把水煮沸 boilWater(); //步骤二:冲饮料 brew(); //步骤三:把饮料倒进杯子 pourInCup(); //步骤四:加调味料 if(wantCondiments()){ addCondiments(); } } /** * Hook:钩子函数,提供一个默认或者空的实现 * 具体的子类可以自行决定能否挂钩以及如何挂钩,即能否重写父类的钩子函数 * 根据个人喜好,能否加入调料 * @return */ protected boolean wantCondiments() { return true; } /** * 基本方法:把水煮沸 * 对所有子类,是一个共同的行为,不需要向子类开放;将变化的东西放在高层代码中。 */ private void boilWater() { System.out.println("把水煮沸"); } /** * 基本方法:将饮料倒入杯中 */ private void pourInCup() { System.out.println("将饮料倒入杯中"); } /** * 不同的情况,具体的实现不同,设计为笼统方法,需要在子类中可见,以便子类复写,提供具体的实现。 * 笼统的基本方法:加入调料 */ protected abstract void addCondiments(); /** * 笼统的基本方法:泡饮料 */ protected abstract void brew();}在OrangeJuice中重写钩子函数时:
package com.hcx.pattern.template;/** * 具体子类,提供了橙汁的具体实现 * @author HCX * */public class OrangeJuice extends Drinks{ @Override protected void brew() { System.out.println("准备橙子和榨汁机,把橙子丢入机器中榨汁"); } @Override protected void addCondiments() { System.out.println("加入糖浆"); } /** * 重写父类的钩子方法 * 不加入任何调料,纯正的橙汁 */ @Override protected boolean wantCondiments() { return false; }}测试类打印结果:
咖啡制备中把水煮沸用沸水冲泡咖啡将饮料倒入杯中加入糖和牛奶咖啡好了*************************************橙汁制备中把水煮沸准备橙子和榨汁机,把橙子丢入机器中榨汁将饮料倒入杯中橙汁好了总结:
模版模式总结.png适用场景:
优点:
缺点:
分析解决各种日志
需求分析:
日志需求分析.png可笼统为如下的步骤:
根据不同的情况,在变化部分的前后提供少量函数来提供扩展。