设计模式行为型
模板方法
定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用模板方法的意图是:定义一个操作算法中的骨架,将一些步骤延迟到子类当中。使得子类不改变算法结构,可重新定义该算法的一些步骤。
简单说来就是:父类实现算法不变的部分以避免代码重复,将特定的可变的行为留给子类实现,也就是所谓的“重分解以一般化”原则。
角色:
抽象模板(Abstract Template)角色:在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
具体模板(Concrete Template)角色:它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。
优点
1、模板方法模式在定义了一组算法,将具体的实现交由子类负责。
2、模板方法模式是一种代码复用的基本技术。
3、模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,是的系统更加庞大。
使用场景:
多个子类有公有的方法,并且逻辑基本相同时。
重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。
类图
模板方法是常用的一种代码复用的基本技巧。
代码:
public interface ITemplate {
public void question();
public void answer();
}
public abstract class Template implements ITemplate{
public abstract void question();
public abstract void answer();
public void tm() {
question();
answer();
}
}
public class Concrete extends Template{
@Override
public void question() {
// TODO Auto-generated method stub
System.out.println("question1?");
}
@Override
public void answer() {
// TODO Auto-generated method stub
System.out.println("answer1:A");
}
public static void main(String[] args) {
Template t = new Concrete();
t.tm();
}
}
模板模式有两大作用:复用和扩展。其中,复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。实际上,还有另外一个技术概念,也能起到跟模板模式相同的作用,那就是回调(Callback)
public interface ICallBack {
void call();
}
public class Template {
public void process(ICallBack callBack) {
callBack.call();
}
}
public class Context {
public static void main(String[] args) {
Template template = new Template();
template.process(new ICallBack() {
@Override
public void call() {
System.out.println("call");
}
});
}
}
应用场景上来看,同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的。而异步回调跟模板模式有较大差别,更像是观察者模式。从代码实现上来看,回调和模板模式完全不同。回调基于组合关系来实现,把一个对象传递给另一个对象,是一种对象之间的关系;模板模式基于继承关系来实现,子类重写父类的抽象方法,是一种类之间的关系。从实际考虑组合优于继承。实际上,这里也不例外。在代码实现上,回调相对于模板模式会更加灵活,主要体现在下面几点。像 Java 这种只支持单继承的语言,基于模板模式编写的子类,已经继承了一个父类,不再具有继承的能力。回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类。如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。