设计模式之模板方法模式
1. 概括
如果有多个类中有着大致相同的算法,只是有算法中有着细微的不同,那么可以使用模板方法模式
模式方法模式在父类中定义了一系列的算法框架,允许子类在不修改结构的情况下,重写算法的特定步骤
模板方法建议把一系列算法,抽取成一个个的步骤,步骤即为方法,这些方法如果是每个子类共有步骤,可以是具体方法;步骤细节不同之处,声明成抽象方法,让每个子类单独去实现 (不包括模板方法)
2. 结构
- 抽象类:定义了一个模板方法,一系列基本方法
- 模板方法:用特定的步骤去调用基本方法,不可被重写
- 基本方法:每个子类的算法公共逻辑是有实现的方法,不同的逻辑为抽象方法
- 钩子方法:一般用于逻辑判断,返回值是bool类型,可以是有实现体的方法,也可是抽象方法
- 具体类:继承或实现抽象类,实现其中的方法
3. 使用场景
- 当希望客户端实现某个特定的算法步骤,而不是其结构
- 当多个类有着相似的步法步骤,只有一些细微的不同
4. 优缺点
- 优点:
- 开闭原则
- 提高代码复用
- 缺点:
- 父类的代码中【反向调用】子类,增加代码阅读难度
- 子类【有可能】会重写父类中,已经有实现体的方法,而违反里氏替换原则
5. 实现
- 在银行办理业务时,取号、排队、办理业务、评分这些步骤中
- 取号、排队、评分,这些是相同的,步骤是相同的,而办理的业务可能不同
- 取号、排队、评分为有实现体的方法,办理业务方法为抽象方法,用一个模板方法去按顺序调用这些方法
- 抽象类
public abstract class BankService {
public void takeNo() {
System.out.println("取号");
}
public void lineUp() {
System.out.println("排队");
}
// 具体不同的算法
public abstract void serviceHandle();
public void score() {
System.out.println("评分");
}
// TODO 模板方法
public final void service() {
takeNo();
lineUp();
serviceHandle(); // TODO 【要点】 由父类调用子类具体实现的方法【反向调用】
score();
}
}
- 具体类
public class BankDeposit extends BankService{
@Override
public void serviceHandle() {
System.out.println("存款业务");
}
}
public class BankTransfer extends BankService{
@Override
public void serviceHandle() {
System.out.println("转账业务");
}
}
- 调用
public class Client {
public static void main(String[] args) {
BankService service = new BankTransfer();
// BankService service = new BankDeposit();
service.service();
}
}
6. 在JDK中的应用
public abstract class InputStream implements Closeable {
// 子类重写的具体方法
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//模板方法,去调用上面的方法
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read(); // 反向调用
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}