模板方法模式(Template Method Pattern)也被称为模板模式(Template Pattern),是在 GoF 23 种设计模式中定义了的行为型模式。
模板方法模式 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
~
本片文章内容包括:关于模版方法模式、观察者模式 Demo、模版方法模式的应用(InputStream 中的模版方法模式)
模板方法模式(Template Method Pattern)也被称为模板模式(Template Pattern),是在 GoF 23 种设计模式中定义了的行为型模式。
模板方法模式 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式 核心:处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。因此,我们采用模版方法模式,将这个节点的代码实现转移给子类完成。即:处理步骤在父类中定义好,具体的实现延迟到子类中定义。
模版方法模式主要由 2 种角色构成:
抽象模板类,由一个模板方法和若干个基本方法构成:
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法;
基本方法:是整个算法中的一个步骤,包含以下几种类型:
# 模版方法模式的优点
# 模版方法模式的缺点
炒菜的步骤是固定的,我们可以将其简化为倒油、热油、下蔬菜、下酱料、翻炒等步骤,现通过模板方法模式来用代码模拟。
Demo 类图如下:
# AbstractClass 抽象模板类
public abstract class AbstractClass {/*** 模板方法*/public final void cookProcess() {//第一步:倒油this.pourOil();//第二步:热油this.heatOil();//第三步:倒蔬菜this.pourVegetable();//第四步:倒调味料this.pourSauce();//第五步:翻炒this.fry();}/*** 第一步:倒油 具体方法,直接实现*/public void pourOil() {System.out.println("倒油");}/*** 第二步:热油 具体方法,直接实现*/public void heatOil() {System.out.println("热油");}/*** 第三步:倒蔬菜 抽象方法*/public abstract void pourVegetable();/*** 第四步:倒调料 抽象方法*/public abstract void pourSauce();/*** 第五步:翻炒 具体方法,直接实现*/public void fry() {System.out.println("翻炒");}
}
# ConcreteClass 具体子类
public class ConcreteClass_BaoCai extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是辣椒");}
}
public class ConcreteClass_CaiXin extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是蒜蓉");}
}
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();}
}
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;}
}
在 InputStream 父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取 len 个字节数据。具体如何读取一个字节数据呢?由子类实现。