1.定义
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
2.结构
模板方法(Template Method)模式包含以下主要角色:
3.案例实现
/**
* @author 晓风残月Lx
* @date 2023/7/21 16:47
* 抽象类(定义模板方法和基本方法)
*/
public abstract class AbstractClass {
// 模板方法 加上final不让子类改变方法结构
public final void cookProcess() {
// 第一步,倒油
this.pourOil();
// 第二步,热油
this.heatOil();
// 第三步,倒蔬菜
this.pourVegetable();
// 第四步,倒调味料
this.pourSauce();
// 第五步,翻炒
this.fry();
}
// 抽象方法
public abstract void pourSauce();
public abstract void pourVegetable();
// 具体方法
private void fry() {
System.out.println("翻炒");
}
private void heatOil() {
System.out.println("热油");
}
private void pourOil() {
System.out.println("倒油");
}
}
/**
* @author 晓风残月Lx
* @date 2023/7/25 0:46
* 炒包菜类 具体的类
*/
public class ConcreteClass_BaoCai extends AbstractClass {
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
}
/**
* @author 晓风残月Lx
* @date 2023/7/25 0:46
* 炒菜芯类 具体的类
*/
public class ConcreteClass_CaiXin extends AbstractClass {
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜芯");
}
}
/**
* @author 晓风残月Lx
* @date 2023/7/25 0:48
*/
public class Client {
public static void main(String[] args) {
// 炒包菜
// 创建对象
ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
baoCai.cookProcess();
// 炒菜芯
ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
caiXin.cookProcess();
}
}
4.优缺点
优点:
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
5.使用场景
- 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
- 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
6.JDK源码解析
InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read()
方法,如下:
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(); //调用了无参的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;
}
}
从上面代码可以看到,无参的 read()
方法是抽象方法,要求子类必须实现。而 read(byte b[])
方法调用了 read(byte b[], int off, int len)
方法,所以在此处重点看的方法是带三个参数的方法。
在该方法中第18行、27行,可以看到调用了无参的抽象的 read()
方法。
总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现。