Template 模板方法设计模式(参考《java与设计模式》)
在java的各种个样设计模式中,大部分都通过委派来实现,尽量避免继承,因为继承会破坏封装,及时用继承,也是接口继承,类继承很少见,但是模板方法却是为数不多使用继承类的。
为什么要有模板方法?
顶层设计人员可以更加专注于整体架构的设计,而将具体子类实现交由程序员来完成。子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法的顶层逻辑。
模板方法如何实现?
简单的说就是一个AbstractClass 在一个方法中定义一个业务中各个操作的顺序,然后在操作中声明一些抽象函数,然后子类继承后,实现每个抽象函数。
模板方法与基本方法?
模板方法:
模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或者一个总行为的方法,在抽象类中定义,子类直接继承不能修改
基本方法:
可分为三类
抽象方法:子类必须自己实现
具体方法:父类设置为final 子类不得修改
钩子方法:子类可以选择是否扩展
ps:对于是用钩子方法还是用抽象方法,模板方法的设计理念是尽量减少必须由子类置换掉的基本数目。
类图
具体代码
package com.company; abstract public class AbstractClass { public void TemplateMethod(){ doOperation1(); doOperation2(); doOperation3(); } //因为只有子类继承和使用,因此这里使用protected修饰 protected abstract void doOperation1(); protected abstract void doOperation2(); //父类实现的方法是通用的方法,不允许再被修改因此使用final修饰 protected final void doOperation3(){ } }
package com.company; public class ConcreteClass extends AbstractClass { @Override protected void doOperation1() { System.out.println("doOperation1();"); } @Override protected void doOperation2() { //子类在重写具体方法时,不能在方法内调用父类已经实现的方法比如doOperation3(), //因为写在内部就打破了父类定义的操作顺序了 System.out.println("doOperation2();"); } }
例题
考虑一个计算存款利息的例子。假设系统需要支持两种存款账户,即货币市场账户,和定期存款账户。这两种账户的存款利息是不同的,因此在计算一个存户的存款利息额,必须区分两种不同的账户。
提示:对于本题是可以通过在一个类中的if-else然后来对不同的类型的账户选择不同的利率,但如果有新的账户加入时,就必须修改这个类,这就违背了开放封闭原则,但如果采用模板方法,有新的账户加入时只需继承AbstractClass然后实现对应的抽象方法即可,同时银行计算利息的过程是确定的,只是中间不同环节会有细微的差别。
UML
代码:
package com.company; abstract public class Account { protected String accoutNumber; public Account() { accoutNumber=null; } public Account(String accountNumber) { this.accoutNumber=accountNumber; } public final double calculateInterest(){ double interestRate=doCalculateInterestRate(); String accountType=doCalculateAccountType(); double amount=calculateAmount(accountType,accoutNumber); return amount*interestRate; } abstract protected String doCalculateAccountType(); abstract protected double doCalculateInterestRate(); final public double calculateAmount(String accountType,String accountNumber) { //此处应该根据 账户类型和账号返回该账号对应数据库中的金额,简化程序使用固定值 return 7243.00D; } }
package com.company; public class MoneyMarketAccount extends Account { @Override protected String doCalculateAccountType() { return "Money Market"; } @Override protected double doCalculateInterestRate() { return 0.045; } }
package com.company; public class CDAccount extends Account { @Override protected String doCalculateAccountType() { return "Certificate of Deposite"; } @Override protected double doCalculateInterestRate() { return 0.065D; } }
package com.company; public class Client { private static Account acct=null; public static void main(String[] args) { // write your code here acct=new MoneyMarketAccount(); System.out.println("Interest from Money Market account "+acct.calculateInterest()); acct=new CDAccount(); System.out.println("Interest from CD account "+acct.calculateInterest()); } }
模板方法在Java中的具体应用
HttpServlet:
Servlet接口含有三个方法
GenericServlet继承Servlet,实现了init,destroy,但是没有实现service。
HttpServlet继承GenericServlet,任然是抽象类,但是实现了service
子类可以继承HttpServlet然后实现七个do方法来完成不同的操作,因此HttpServlet是模板方法的应用。