什么是模板方法模式
模板方法模式定义了一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构就可以重定义该算法的某些特定步骤。
模板方法模式主要包含几下几个角色:
AbstractClass(抽象类):在抽象类中定义了一系列基本操作,这些操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在子类中可以重定义或实现这些步骤。同时在抽象类中实现了一个模板方法用于定义一个算法的框架,模板方法不仅可以调用抽象类中实现的方法,也可以调用子类中实现的基本方法。
ConcreteClass(具体类):是抽象类的子类,用于实现父类中声明的抽象方法,也可以覆盖父类中已经实现的具体方法。
模板方法模式的优缺点
优点
- 在父类中定义模板方法,子类实现细节的处理并不会改变模板方法中的执行步骤。
- 提取了公共逻辑放到父类中,提高代码复用性。
- 可在子类中重定义父类中的方法,提高了扩展性。
- 增加新的子类方便,符合单一职责原则和开闭原则。
缺点
- 需要为每一个基本方法的不同实现提供一个子类,如果父类中可变基本方法过多,将会导致类的个数增加。系统更加庞大,设计更加抽象。
模板方法模式的应用场景
- 对一些复杂的算法进行分割,将算法中固定不变的部分设计为模板方法和父类具体方法,一些可变的部分由子类实现。
- 多个类中有公共行为应该被提取出来到父类中减少代码重复。
- 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
模板方法模式的案例
// 抽象类
public abstract class SoyaMilk {
final void make() {
select();
if (customerWantCondiments()) {
addCondiments();
}
soak();
beat();
}
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
abstract void addCondiments();
void soak() {
System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");
}
boolean customerWantCondiments() {
return true;
}
}
// 具体类
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println(" 加入上好的红豆 ");
}
}
public class PureSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
}
@Override
boolean customerWantCondiments() {
return false;
}
}
public class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println(" 加入上好的花生 ");
}
}
public static void main(String[] args) {
//制作红豆豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
System.out.println("----制作花生豆浆----");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
System.out.println("----制作纯豆浆----");
SoyaMilk pureSoyaMilk = new PureSoyaMilk();
pureSoyaMilk.make();
}
模板方法模式在源码中的应用
InputStream
// 抽象类
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 class FileInputStream extends InputStream {
public int read() throws IOException {
return read0();
}
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
......
}