不知道为什么,这几天对Java中的设计模式非常感兴趣,恰巧呢这几天公司的开发任务还不算太多,趁着有时间昨天又把模板方法模式深入学习了一下,做了一个客户在不同银行计息的小案例,感触颇深,今天给各位分享一下,可能有些常识我在程序中运用的不是很到位,希望各位谅解。
模板方法模式呢,按我意思理解:就是将完成某件事情固定不变的步骤设计成模板类用final修饰的方法,然后将不确定的业务逻辑设计成抽象的方法,目的就是让子类继承并且复写该抽象方法,能够为了实现可扩展性。官方的说法:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。
模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成。
好了,读到这儿你估计也懵懵逼逼的,我还是喜欢用事实说话,看码。
【案例一】:就是同一个客户在不同银行分别储蓄了一定金额的存款,2年之后他想知道在每个银行分别能拿到多少本息?(本息=本金+利息)(利息=本金*利率*存期*100%)
设计思想:
通过分析得出,不管他想知道那个银行的本息,都是通过本金+利息的方法得到的,但是问题来了,每个银行计算利息的方式是不同的,当然不是说方式不同而是每个银行根据客户的存期参与计算的利率是不相同的。那么可以根据模板方法模式,可以将计算本息的过程设计成用final修饰的方法,而计算利息的过程可以设计成抽象的方法,然后可以由每个银行类通过继承模板类并复写计算利息的方法来计算出每个银行的利息,最后得出本息。
实现过程:
模板类,将计算本息的过程设计成模板方法,用关键词final修饰(因为用final修饰的方法是不能被复写的,final修饰的变量是不能被重新赋值的,final修饰的类是不能被继承的),然后将计算利息的过程设计成抽象方法,任由子类复写,最后在模板方法中会调用抽象方法,这也是模板设计模式的特性。
1 /** 2 * 模板类:每个银行获取本息的方式是固定不变的,但是利息是通过每个银行自己的规定的利率计算得到的。 3 * 利息=本金*存期*利率 4 * 本息=本金+利息 5 */ 6 abstract class Interest{ 7 /** 8 * 获取本息 9 * @param capital:本金 10 * @return 11 */ 12 public final Double getCapital(Double capital){ 13 return capital+getInterest(); 14 } 15 //获取利息 16 public abstract Double getInterest(); 17 }
由建设银行和浦发银行分别继承上面的模板类,然后通过各自的利率规则计算出客户的利息,其实这里我的程序并不灵活,假设在实际开发场景中,客户的本金和存期都是由数据库查询出来的。(走到这儿我还有一个小问题想请教各位,就是一直想把每个银行计算利息的那段代码再优化一下,switch语句那块代码把它也想放到final修饰的方法中,但是昨天下班脑子实在转不动了,后期有机会再优化吧)
1 /** 2 * 建设银行 3 */ 4 class JSBank extends Interest{ 5 //获取指定用户的本金、存期等数据 6 public double capital = 38654.6; 7 public int time = 24; 8 //计算客户利息 9 @Override 10 public Double getInterest() { 11 int num = time/12; //根据用户存期得到年利率 12 switch(num){ 13 case 1: 14 return capital*0.0175*num; //一年 15 case 2: 16 return capital*0.0225*num; //二年 17 case 3: 18 return capital*0.0275*num; //三年 19 case 5: 20 return capital*0.0275*num; //五年 21 } 22 return capital*0.0135; //未满一年 23 } 24 } 25 26 /** 27 * 浦发银行 28 */ 29 class PFBank extends Interest{ 30 //获取指定用户的本金、存期等数据 31 public double capital = 38654.6; 32 public int time = 24; 33 @Override 34 public Double getInterest() { 35 int num = time/12; //根据用户存期得到年利率 36 switch(num){ 37 case 1: 38 return capital*0.02*num; //一年 39 case 2: 40 return capital*0.024*num; //二年 41 case 3: 42 return capital*0.028*num; //三年 43 case 5: 44 return capital*0.028*num; //五年 45 } 46 return capital*0.0135; //未满一年 47 } 48 }
最后来测试一下,我这里的客户是科比 布莱恩特(因为他一直是我的偶像,我喜欢他,喜欢他的性格,喜欢他的一切,他的故事一直在激励着我,一直感谢我生命里有他,请允许我附一张他的图片,哈哈哈~~~):
1 /** 2 * 模板设计模式 3 * 需求:科比分别在建设银行和浦发银行等存蓄一定金额,2年后他想知道他在每个银行的本息是多少? 4 */ 5 6 public class TemplateMethodLiXi { 7 8 public static void main(String[] args) { 9 //建行 10 JSBank js = new JSBank(); 11 Double jsPrincipalAndInterest = js.getCapital(js.capital); 12 System.out.println("建设银行(科比):"+jsPrincipalAndInterest); 13 //浦行 14 PFBank pf = new PFBank(); 15 Double pfPrincipalAndInterest = pf.getCapital(pf.capital); 16 System.out.println("浦发银行(科比):"+pfPrincipalAndInterest); 17 } 18 19 }
【案例二】:计算一段代码的运行时长?
这个案例我不多说设计思想和实现过程,因为实现理念是类似的,请各位赏脸读读鄙人写的代码,它主要就是计算子类继承模板类,然后子类复写抽象方法并实现自己的业务逻辑,最后还是通过模板方法调用抽象方法来灵活达到计算不固定代码块的运行时间。
1 /** 2 * 模板方法模式 3 * 需求:计算自定义类中某个方法(可变的)的运行时长? 4 */ 5 6 public class TemplateMethod { 7 public static void main(String[] args) { 8 MyCode my = new MyCode(); 9 Long runtime = my.getTime(); //调用父类的方法获取子类方法的运行时长 10 System.out.println("运行时间:"+runtime); 11 } 12 } 13 14 /** 15 * 模板类:计算一段未知代码的运行时长。 16 */ 17 abstract class GetCodesRunTime{ 18 /** 19 * 专门获取一段代码的运行时间 20 * @return 21 */ 22 public final Long getTime(){ 23 long startTime = System.currentTimeMillis(); //开始时间 24 runCode(); //调用本类抽象方法 25 long endTime = System.currentTimeMillis(); //终止时间 26 27 return endTime-startTime; 28 } 29 //由子类复写,然后计算子类方法中代码的运行时长 30 public abstract void runCode(); 31 } 32 33 /** 34 *自定义代码块,继承模板类,然后复写父类的扩展方法 35 */ 36 class MyCode extends GetCodesRunTime{ 37 @Override 38 public void runCode() { 39 for(int i=0; i<=20000; i++){ 40 System.out.println(i); 41 } 42 } 43 }
设计模式的学习还在继续,后期还会给各位分享其他设计模式的学习心得和案例的实现过程。