四十六:模式方法模式
模式方法模式是类的行为模式,准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方法实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意.它是基于继承的代码复用的基本技术。在定义新的子类的时候,应当考虑哪些操作是必须置换掉的,哪些操作是可以置换掉的,以及哪些操作是不可以置换掉的,使用模板方法模式可以使这些责任变得清晰.
一:模板方法模式中的角色
这里涉及到两个角色
(A)抽象模板(Abstract Template)角色,它有如下责任:
(a)定义一个或多个抽象操作,以便让子类实现,这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
(b)定义并实现一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现.
(B)具体模板(Concrete Template)角色有如下责任:
实现模板方法
这个模式应用在BaseEntity的设计中.......
二:一个例子
考虑一个计算存款利息的例子。假设系统需要支持两种存款帐号:货币市场(MoneyMarket)帐号和定期存款(CD)帐号。这两种帐号的存款利息是不同的,因此在计算一个存户的存款利息时,必须区分两种不同的帐号类型
这个系统总行为应当是计算出利息,这也就决定了作为一个模板方法模式的顶级逻辑应当是利息计算。由于利息计算涉及到两个步骤:一是确定帐户类型,二是确定利息的百分比,因此系统需要两个基本方法:一个给出帐号类型,二是确定利息的百分比,因此系统需要两个基本方法:一个基本方法给出帐号种类,另一个方法给出利息百分比,这两个方法构成具体逻辑,因为帐号类型不同,所以具体逻辑会有所不同.显然系统需要一个抽象角色给出顶级行为的实现,而将两个作为细节步骤的基本方法留给具体子类实现.
package cai.milenfan.basic.test; /*这个抽象角色给出了顶级逻辑的骨架,即calculateInterest()的内容,只是将内容的具体步骤委派给不同的基本方法, *在这里分别是doCalculateAccountType()和doCalculateInterestRate(),而这两个抽象方法则留给具体子类实现 */ /*final*/ public abstract class Account {//final类没有子类protected String accountNumber; public Account() { accountNumber = null; } public Account(String accountNumber) { this.accountNumber = accountNumber; } //模板方法,计算利息数额 final public double calculateInterest()//final方法不能被重写 { double interestRate = doCalculateInterestRate(); String accountType = doCalculateAccountType(); double amount = calculateAmount(accountType, accountNumber); return amount * interestRate; } //模板方法,留给子类实现 abstract protected String doCalculateAccountType() ; //模板方法,留给子类实现 abstract protected double doCalculateInterestRate() ; //基本方法,已经实现 final public double calculateAmount(String accountType, String accountNumber) { //retrieve amount from database...here is only a mock-up return 7243.00D; } } package cai.milenfan.basic.test; public class MoneyMarketAccount extends Account{ public String doCalculateAccountType() { return "Money Market"; } public double doCalculateInterestRate() { return 0.045D; } //public double calculateInterest(){},final方法不能被重写,但可以被重载} package cai.milenfan.basic.test; public class CDAccount extends Account{ public String doCalculateAccountType() { return "Certificate of Deposite"; } public double doCalculateInterestRate() { return 0.065D; } } package cai.milenfan.basic.test; public class Client{ private static Account acct = null; public static void main(String[] args) { acct = new MoneyMarketAccount(); System.out.println("Interest earned from Money Market account = " acct.calculateInterest()); acct = new CDAccount(); System.out.println("Interest earned from CD account = " acct.calculateInterest()); } }