设计模式十五之模版方法模式
在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
1. 模式的定义与特点
1.1 模式的定义
模版方法模式(Template):定义一个操作的算法骨架,将算法的一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义该算法的某些特定步骤。
1.2 模式的特点
模版方法模式的优点有:
1. 它封装了不变部分,扩展了可变部分。它吧不变的部分封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展;
2. 它在父类中提取了公共部分,便于代码的复用;
3. 部分方法由子类实现,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
模版方法模式的缺点有:
1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象;
2. 继承关系自身缺点,如果父类增加新的抽象方法,所有子类都要改变。
1.3 模式的使用场景
1. 一次性实现一个算法的不变部分,并将可变行为留给子类来实现;
2. 各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复。
2. 模式的结构与实现
2.1 模式的结构
模版方法模式的主要角色如下:
1. 抽象父类(Abstract Class):负责给出一个算法的轮廓和骨架,它由一个模版方法和若干基本方法组成,这些方法的定义如下:
1.1 模版方法:定义了算法的骨架,按某种顺序调用其包含的基本方法;
1.2 基本方法:是整个算法中的一个步骤,包含以下几种类型:
1.2.1 抽象方法:在抽象类中声明,由子类去实现;
1.2.2 具体方法:在抽象类中已实现,可在子类中继承或重写;
1.2.3 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
2. 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法。
2.2 模式的实现
抽象父类
/**
* 抽象父类 - 银行业务
*/
public abstract class BankService {
protected final void business() {
takeNumber();
lineUp();
work();
if (needScore()) {
score();
}
}
final void takeNumber() {
System.out.println("首先,取号...");
}
final void lineUp() {
System.out.println("其次,排队中...");
}
/**
* 办理业务
*/
abstract void work();
/**
* 钩子方法:是否评分
* @return
*/
protected boolean needScore() {
return false;
}
final void score() {
System.out.println("最后,给工作人员评分...");
}
}
具体子类
/**
* 老板转账
*/
public class Boss extends BankService {
@Override
void work() {
System.out.println("老板来办转账业务...");
}
@Override
protected boolean needScore() {
return true;
}
}
/**
* 工人存钱
*/
public class Worker extends BankService {
@Override
void work() {
System.out.println("工人办理存钱业务...");
}
}
客户端
public class Client {
public static void main(String[] args) {
Boss boss = new Boss();
boss.business();
Worker worker = new Worker();
worker.business();
}
}
# 运行结果如下:
首先,取号...
其次,排队中...
老板来办转账业务...
最后,给工作人员评分...
首先,取号...
其次,排队中...
工人办理存钱业务...
3. 模式在开源软件中的应用
3.1 java.util.AbstractList 类
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public boolean add(E e) {
add(size(), e);
return true;
}
abstract public E get(int index);
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
public void clear() {
removeRange(0, size());
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
}
在 AbstractList 中不仅已经实现了部分方法,而且还声明了很多空方法待子类去重写,相同的还有 AbstractSet 和 AbstractMap 等。