定义:在一个方法中定义了一个算法的骨架或者步骤,而将一些步骤延迟到子类中去实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某一些步骤。
模板方法在基类中定义了一个操作的流程顺序,能够保证该步骤按序进行,有一些步骤的具体实现在基类中已经声明,而将一些变化的步骤的具体实现交给了子类去实现,从而就达到了延迟一些步骤到子类中,模板方法一个最大的好处就是能够设定一个业务流程能够按照一定严格的顺序执行,控制了整个算法的执行步骤。
这个方法将算法定义成一组步骤,其中凡是想让子类进行自定义实现的步骤,均定义为抽象方法。抽象基类的特点是,一般将模板方法设置为final,这样防止子类覆盖该算法的步骤,将一些相同的操作或步骤直接在基类中实现,将一些变化的步骤设置为抽象由子类去完成。
代码实例
:
假如要泡杯茶和咖啡,要想泡咖啡则需要先将水煮沸,然后用沸水冲泡咖啡,将咖啡倒进杯子,加糖和牛奶。要想泡茶叶则需要把水煮沸,然后用沸水冲茶叶,将茶倒入杯子,放点柠檬。
通过分析,这些操作基本上都是四步骤,顺序都是固定的,且有相同的步骤,则就可以利用模板方法来将泡饮料的过程制作一个算法的骨架,将变化的部分抽象出来,让具体的子类去实现。
抽象超类,这个类是负责泡饮料的基类,设置了算法的骨架
package com.whut.modelmethod;
//模板方法
abstract class BeverageMake {
//final可以防止子类更改覆盖该算法,这样可以保证算法步骤不被破坏
public final void prepareRecipe()
{
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
//烧水
public void boilWater()
{
System.out.println( "Now start to boiling water");
}
//饮料导入杯子汇总
public void pourInCup()
{
System.out.println( "pour the beverage into the cup");
}
}
//模板方法
abstract class BeverageMake {
//final可以防止子类更改覆盖该算法,这样可以保证算法步骤不被破坏
public final void prepareRecipe()
{
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
//烧水
public void boilWater()
{
System.out.println( "Now start to boiling water");
}
//饮料导入杯子汇总
public void pourInCup()
{
System.out.println( "pour the beverage into the cup");
}
}
package com.whut.modelmethod;
public class Tea extends BeverageMake {
@Override
void brew() {
// TODO Auto-generated method stub
System.out.println( "boil the tea in the water");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println( "put some condiments into the tea");
}
}
public class Tea extends BeverageMake {
@Override
void brew() {
// TODO Auto-generated method stub
System.out.println( "boil the tea in the water");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println( "put some condiments into the tea");
}
}
package com.whut.modelmethod;
public class MakeTest {
public static void main(String[] args) {
Tea tea= new Tea();
tea.prepareRecipe();
}
}
public class MakeTest {
public static void main(String[] args) {
Tea tea= new Tea();
tea.prepareRecipe();
}
}
当在模板方法中某一些步骤是可选的时候,也就是该步骤不一定要执行,可以由子类来决定是否要执行,则此时就需要用上钩子。钩子是一种被声明在抽象类中的方法,但一般来说它只是空的或者具有默认值,子类可以实现覆盖该钩子,来设置算法步骤的某一步骤是否要执行。钩子可以让子类实现算法中可选的部分,让子类能够有机会对模板方法中某些一即将发生的步骤做出反应。
重写上面的代码
:
这次茶叶泡好后,加不加东西由子类去决定
。
超类
:
package com.whut.modelmethod;
//模板方法
abstract class BeverageMake {
//final可以防止子类更改覆盖该算法,这样可以保证算法步骤不被破坏
public final void prepareRecipe()
{
boilWater();
brew();
pourInCup();
if(hookCondiments())
addCondiments();
}
abstract void brew();
abstract void addCondiments();
//烧水
public void boilWater()
{
System.out.println( "Now start to boiling water");
}
//饮料导入杯子汇总
public void pourInCup()
{
System.out.println( "pour the beverage into the cup");
}
//加入了钩子,来让子类决定是否执行该步骤
public boolean hookCondiments()
{
return true;
}
}
//模板方法
abstract class BeverageMake {
//final可以防止子类更改覆盖该算法,这样可以保证算法步骤不被破坏
public final void prepareRecipe()
{
boilWater();
brew();
pourInCup();
if(hookCondiments())
addCondiments();
}
abstract void brew();
abstract void addCondiments();
//烧水
public void boilWater()
{
System.out.println( "Now start to boiling water");
}
//饮料导入杯子汇总
public void pourInCup()
{
System.out.println( "pour the beverage into the cup");
}
//加入了钩子,来让子类决定是否执行该步骤
public boolean hookCondiments()
{
return true;
}
}
package com.whut.modelmethod;
public class Tea extends BeverageMake {
@Override
void brew() {
// TODO Auto-generated method stub
System.out.println( "boil the tea in the water");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println( "put some condiments into the tea");
}
//设置不需要加饮料,这样就可以控制算法的某一个步骤不执行
@Override
public boolean hookCondiments()
{
return false;
}
}
public class Tea extends BeverageMake {
@Override
void brew() {
// TODO Auto-generated method stub
System.out.println( "boil the tea in the water");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
System.out.println( "put some condiments into the tea");
}
//设置不需要加饮料,这样就可以控制算法的某一个步骤不执行
@Override
public boolean hookCondiments()
{
return false;
}
}
设计原则:
别调用我们,我来调用你们。
这种原则主要就是,我们允许底层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么样来使用这些底层组件
。
要点:
1)钩子是一种方法,它在抽象类中不做事,或者是默认的事情,子类可以选择覆盖它
2)为了防止子类改变模板方法中的算法骨架,一般将模板方法声明为final
3)策略模式和模板方法都是用于封装算法,前者是利用组合和委托模型,而后者则是继承
模版方法模式的结构
模版方法模式由一个抽象类和一个(或一组)实现类通过继承结构组成,抽象类中的方法分为三种:
- 抽象方法:父类中只声明但不加以实现,而是定义好规范,然后由它的子类去实现。
- 模版方法:由抽象类声明并加以实现。一般来说,模版方法调用抽象方法来完成主要的逻辑功能,并且,模版方法大多会定义为final类型,指明主要的逻辑功能在子类中不能被重写。
- 钩子方法:由抽象类声明并加以实现。但是子类可以去扩展,子类可以通过扩展钩子方法来影响模版方法的逻辑。
- 抽象类的任务是搭建逻辑的框架,通常由经验丰富的人员编写,因为抽象类的好坏直接决定了程序是否稳定性。
实现类用来实现细节。抽象类中的模版方法正是通过实现类扩展的方法来完成业务逻辑。只要实现类中的扩展方法通过了单元测试,在模版方法正确的前提下,整体功能一般不会出现大的错误。
模版方法的优点及适用场景
容易扩展。一般来说,抽象类中的模版方法是不易反生改变的部分,而抽象方法是容易反生变化的部分,因此通过增加实现类一般可以很容易实现功能的扩展,符合开闭原则。
便于维护。对于模版方法模式来说,正是由于他们的主要逻辑相同,才使用了模版方法,假如不使用模版方法,任由这些相同的代码散乱的分布在不同的类中,维护起来是非常不方便的。
比较灵活。因为有钩子方法,因此,子类的实现也可以影响父类中主逻辑的运行。但是,在灵活的同时,由于子类影响到了父类,违反了里氏替换原则,也会给程序带来风险。这就对抽象类的设计有了更高的要求。
在多个子类拥有相同的方法,并且这些方法逻辑相同时,可以考虑使用模版方法模式。在程序的主框架相同,细节不同的场合下,也比较适合使用这种模式。
转自:http://computerdragon.blog.51cto.com/6235984/1167937