模板方法模式是GOF设计模式中很典型的设计模式,其意图是由抽象父类控制顶级逻辑,并把基本操作的实现推迟到子类去实现,这是通过继承的手段来达到对象的复用。Spring模板方法模式实际是模板方法模式和回调模式的结合,Spring几乎所有的外接扩展都采用这种模式,如JNDI,JMS,JCA的 CCI,JDBC,ORM中的Hiberate3、jdo、Toplink等扩展都采用模板方法模式来扩展。
开闭原则是指一个软件实体应该对扩展开放,对修改关闭。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。如图所示,面向接口编程,client只需调用Template接口中的execute方法,在AbstractTemplate抽象类的示例代码所示,实现execute()方法,但在execute()方法中制定所需的大的步骤,调用如subExecute1(),subExecute2(),subExecute3()方法来完成,但AbstractTemplate抽象类把subExecute1(),subExecute2()设置为抽象方法,由子类来实现,subExecute3()给出了缺省实现,也可由子类来覆盖,如SubTemplateImpl2类。
public abstract class AbstractTemplate implements TemplateInterface { public void execute() { subExecute1(); subExecute2(); subExecute3(); } protected abstract void subExecute1(); protected abstract void subExecute2(); protected void subExecute3() {
}
} |
在Spring中也有对经典模板方法模式的运用,以Hibernate3为例说明
AbstractSessionFactoryBean模板方法afterPropertiesSet(),调用抽象方法buildSessionFactory()和缺省实现方法afterSessionFactoryCreation()
public void afterPropertiesSet() throws Exception { SessionFactory rawSf = buildSessionFactory(); this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf); afterSessionFactoryCreation(); } protected abstract SessionFactory buildSessionFactory() throwsException; protected void afterSessionFactoryCreation() throws Exception { } |
在其子类中LocalSessionFactoryBean中则有相关子步骤方法的实现
protected SessionFactory buildSessionFactory() throws Exception { … … //代码太长了 return newSessionFactory(config); } protected void afterSessionFactoryCreation() throws Exception { if (this.schemaUpdate) { DataSource dataSource = getDataSource(); if (dataSource != null) { configTimeDataSourceHolder.set(dataSource); } try { updateDatabaseSchema(); } finally { if (dataSource != null) { // Reset DataSource holder. configTimeDataSourceHolder.set(null); } } } } |
Spring几乎所有的外接扩展,都采用模板方法模式,为了说明的更清楚些,用Hiberate3的扩展举例说明。
HibernateCallback接口,在该接口中只声明了一个方法doInHibernate(Session session),这个方法其实是由子类来实现的子步骤方法
public interface HibernateCallback { Object doInHibernate(Session session) throws HibernateException, SQLException; } |
HibernateTemplate类的execute()方法,值得注意的是execute()方法的一个参数是HibernateCallback接口,这就是Spring模板方法模式的核心和前提,为了清楚模板方法模式,把相关的事务处理及异常处理等代码去掉,这样可以清楚地看出HibernateCallback接口的action.doInHibernate(sessionToExpose)仅仅是其中的一个步骤,并且action就是execute模板方法方法的第一个参数HibernateCallback接口的实际实现类。
public Object execute(HibernateCallback action, booleanexposeNativeSession) throws DataAccessException { previousFlushMode = applyFlushMode(session,existingTransaction); enableFilters(session); Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session)); Object result = action.doInHibernate(sessionToExpose); flushIfNecessary(session, existingTransaction); return result;
} |
在HibernateTemplate类中实现HibernateCallback接口的却是匿名类,如get方法
public Object get(final String entityName, final Serializable id, finalLockMode lockMode) throws DataAccessException {
return execute(new HibernateCallback() { public Object doInHibernate(Session session) throwsHibernateException { if (lockMode != null) { return session.get(entityName, id, lockMode); } else { return session.get(entityName, id); } } }, true); } |
如load方法
public Object load(final Class entityClass, final Serializable id,final LockMode lockMode) throws DataAccessException {
return execute(new HibernateCallback() { public Object doInHibernate(Session session) throwsHibernateException { if (lockMode != null) { return session.load(entityClass, id, lockMode); } else { return session.load(entityClass, id); } } }, true); } |
从get(),load()方法中可以看出其实现HiberateCallback接口的方法都很简单,都是封装Hiberate的session的同名方法。这样有效地减少了子类的个数(如LoadHibernateCallback,GetHibernateCallback,同时当你调用get(),load()方法时要先new或者配置好相应的子类),这时候使用匿名类来实现回调则很方便。
Client来调用get方法,get方法调用模板方法时,就先塞进去一个匿名子类作为参数,由匿名子类来完成相关的子步骤的逻辑,通过模板方法回调匿名子类的方法来实现模板方法模式。
Spring模板方法的扩展,不像经典的模板方法模式那样需要继承模板方法抽象类,而是通过实现回调函数HibernateCallback 接口的doInHibernate方法的匿名类作为参数,直接调用HibernateTemplate的execute()模板方法, 这样其实也是Spring的无侵入设计思想的体现,同时也是“依赖优于继承”设计理念的体现。
public Object sqlExecute(String sql) { return getHibernateTemplate().execute(new HibernateCallback(){ public Object doInHibernate(Session session) { //write youself business logic, using your parameters certainly. return null; }
}, true); } |
Spring模板方法设计模式适用于模板类有很多方法,如HibernateTemplate,数据库操作使用的如此频繁,一个操作就需要通过继承一个子类,这显然不可能,这时使用经典的模板方法模式则显笨重。Spring使用Callback模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性,只需实现某些CallBack就可轻松订制Template。
经典的模板方法设计模式适用于如果每个具体的步骤都需要真正去具体实现,而不是简单的改变参数或设置某个对象就ok的话,使用Callback很难去订制,因为你可能需要传递多个Callback作为参数,并让用户去实现,使用Java的内部类本来就是一个比较丑陋的语法,更何况参数是多个。