在微软的WebCast上, 李建中老师有个设计模式的系列讲座,其中在讲到模板方法(Template Method)曾说:如果你只想学习一种设计模式就学习模板方法吧。由此可见它使用的广泛性。
那么,什么是模板方法模式呢?在解决这个问题前,咱们先来看看模板。提到模板,相信大家马上能够想到一些东西,如ppt的模板,报表导出的excel模板,简历的模板等等,呵呵,使用它们的好处当然是显而易见的:它们可以给我们提供特定的结构和样式,我们就只需关心填充数据内容。将模板的思想发散到编程上,就是我们今天的主题了。
AbstractClass主要是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。这有点像C语言中的一个“钩子(hook)”;同时将公用的代码移植到TemplateMethod中,实现的代码的公用。
我们来看一个模板方法的应用
public class 高校
{
//这就是一个模板方法,定义了算法的骨架
public final void 报到()
{
教务处报到();
缴费();
本院系报到();
教材科发教材();
}
protected abstract void 教务处报到();
protected abstract void 缴费();
protected abstract 专业等信息 本院系报到();
protected abstract 教材 教材科发教材();
}
public class 清华大学 extends 高校
{
protected void 教务处报到()
{
//刷卡
}
protected void 缴费()
{
//银行卡转账
}
protected
{
//系统自动将学生信息转入到院系信息系统
//系统将学生专业班级信息发送到学生手机
}
protected 教材 教材科发教材()
{
//学校物流配送系统送教材
return 教材
}
}
public class 北京大学 extends 高校
{
protected void 教务处报到()
{
//学生签字
//教务处处长签字
//报到完毕
}
protected void 缴费()
{
//到财务处缴纳现金
}
protected 专业等信息 到本院系报到()
{
//见到院书记
}
protected 教材 教材科发教材()
{
//自备袋子到教材科
}
}
以上是两个高校针对教育部颁发的新生报到算法作出自己具体的实现。那么全国有这么多高校又有多少种实现呢?让教育部分别给每所高校规定新生报到算法,这样是不现实的,如果教育部给所有高校规定同一个样子的报到流程就失去了灵活性,毕竟要考虑实际嘛。
通过上面的描述是不是觉得模板方法好像是:上有政策,下有对策?对,模板方法就是上面的这个政策,各个下级有自己对政策中具体步骤的实现。模板方法就像我们的宪法一样,对其他所有法律指定了一些大的框架,要制订新的法律必须依照这个框架。
看过这个例子后,大家可以思考一下,在java中,我们见过这种设计模式的身影吗?哦,搜索一下还真是不少!主要是在一些框架譬如Junit,Struts,Spring等的设计中大量采用。
(1) JUnit中的TestCase以及它的子类就是一个模板方法模式的例子。在TestCase这个抽象类中将整个测试的流程设置好,比如先执行setup方法初始化测试资源,再运行测试方法,然后再tearDown来释放测试资源。但是我们将在setup、tearDown里面作些什么?谁知道呢?!因此,这些步骤的具体实现都会延迟到子类中去,也就是我们实现的测试类中。
(2) Struts框架控制器的核心类RequestProcess里面的process方法也是采用了经典的模板方法模式,里面定义好了流程的步骤,而步骤里面的很多环节,我们都是可以重写的。
(3)Spring用另一种方式实现了Template Method模式,我们来详细解读一下。Spring对Hibernate的调用提供了HibernateDaoSupport的支持,在该类中,有了一个HibernateTemplate类来调用Hiebarnate接口,通过另外一种方式使用Template Method模式,避免了开发者代码中出现诸如Session管理、事务控制等重复代码。
public interface HibernateCallback {
}
就像我们在前面形容的一样HibernateCallback扮演一个“钩子(hook)“的角色,这样用接口和不同的接口实现类替代了Template Method模式中的继承关系同时也将模板方法execute(HibernateCallback action)独立出来。
看看execute的实现:
public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
我们可以看到,在excute方法中做了对Session的管理和事务的控制,而代码:
Object result = action.doInHibernate(sessionToExpose);
则执行实际的数据库操作。
我们看看HibernateTemplate对hibernate的一个get方法的封装:
return execute(new HibernateCallback() {
在该段代码中用匿名类声明了一个接口实现,并将改对象作为参数传给excute方法。如此,便实现了Template Method设计模式。
从整体来看,我觉得有几点可以值得关注:
1.用接口代替继承来抽象出hibernate的不同数据库操作,达到一种更加轻量级的效果
2.将对变动的与不变的相互分离,在本例中不变的是Session和事务管理,变动的是不同的数据库操作
3.通过封装隐藏Template Method模式,对用户的使用是透明的
这样就是一种更优雅的方式实现了TemplateMethod模式。
下面我们来对末班方法模式做一个总结:
1.模板方法设计模式的意图
2.模板方法模式定义及结构
3.模板方法模式与控制反转
4.模板方法模式与开闭原则
5.模板方法模式与对象的封装性
6.
各个模式之间都有联系,模板方法也不例外,她并不是孤立存在的。模板中的那些虚方法实际上都是使用工厂方法设计模式,将父类的执行逻辑延迟到子类。有的时候模板方法里定义算法的步骤会用到策略模式,因为有的时候这个算法不止一种,比如上面的教育部规定新生报到流程这个算法,有可能教育部规定了三四种,那么我们就可以用策略模式封装这几套算法。
7.模板方法的变形