Java 模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并将一些步骤延迟到子类中实现。模板方法模式使得子类可以在不改变算法结构的情况下重定义算法中的某些步骤。
使用场景
- 算法骨架固定:如果一个算法的基本结构已经固定,但具体的实现步骤可能因为不同的场景而不同,这个时候可以使用模板方法模式。
- 实现代码复用:如果有多个类的某些方法结构相似,但是实现细节不同,这个时候可以将这些相同的结构抽象到父类中,由子类来实现不同的细节。
- 简化代码实现:模板方法模式可以将复杂的代码实现分离成几个简单的步骤,从而降低代码实现的难度和复杂度。
- 框架和库的设计:模板方法模式是设计框架和库的重要方式之一,它可以提供统一的接口和标准的实现流程,方便用户进行扩展和定制
代码示例
AbstractClass 是一个抽象类,它定义了算法的骨架,其中 templateMethod() 是模板方法,它定义了算法的流程,由一些抽象方法 primitiveOperation1() 和 primitiveOperation2() 组成。
ConcreteClass 是 AbstractClass 的具体子类,它实现了抽象方法,定义了具体的算法细节。在客户端使用时,创建 ConcreteClass ,然后调用其 templateMethod() 方法,即可完成算法的执行。
代码示例如下:
抽象类,定义算法骨架:
public abstract class AbstractClass {// 模板方法,定义算法流程public final void templateMethod() {primitiveOperation1();primitiveOperation2();}// 抽象方法1,由子类实现public abstract void primitiveOperation1();// 抽象方法2,由子类实现public abstract void primitiveOperation2();
}
具体子类,实现具体的算法细节:
public class ConcreteClass extends AbstractClass {@Overridepublic void primitiveOperation1() {System.out.println("ConcreteClass.primitiveOperation1()");}@Overridepublic void primitiveOperation2() {System.out.println("ConcreteClass.primitiveOperation2()");}
}
客户端使用:
public class Client {public static void main(String[] args) {AbstractClass abstractClass = new ConcreteClass();abstractClass.templateMethod();}
}
需要注意的是,在模板方法模式中,模板方法通常被声明为 final,以防止子类对其进行重写。同时,由于模板方法是一个抽象方法,因此在实现时需要注意不同抽象方法的实现顺序,以确保算法的正确性。
Spring 中的 JdbcTemplate
- 在 JdbcTemplate 类中,定义一个 execute 方法,该方法接收一个 ConnectionCallback 或 StatementCallback 对象作为参数。这些回调对象实现了具体的数据库操作逻辑。
- 在 execute 方法中,根据传入的回调对象类型,创建相应的 PreparedStatement 或 Statement 对象,并调用回调对象的 doInConnection 或 doInStatement 方法执行具体的数据库操作。
- 将数据库连接、PreparedStatement 或 Statement 对象等资源关闭的操作封装在 finally 代码块中,确保资源能够被正确释放。
代码如下:
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = DataSourceUtils.getConnection(obtainDataSource());try {Connection conToUse = createConnectionProxy(con);return action.doInConnection(conToUse);}catch (SQLException ex) {String sql = getSql(action);DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw translateException("ConnectionCallback", sql, ex);}finally {DataSourceUtils.releaseConnection(con, getDataSource());}}
JdbcTemplate 类的 execute 方法接收一个 ConnectionCallback 对象作为参数,然后根据该对象执行具体的数据库操作。这样,JdbcTemplate 就实现了模板模式,将数据库操作的具体逻辑封装在 ConnectionCallback 对象中,而 JdbcTemplate 只负责管理数据库连接和资源释放。
优点
- 代码复用性:模板模式通过将类的共同部分代码抽象出来放在父类中,使得子类只需要实现差异部分,这大大减少了子类的重复代码。
- 可扩展性:当需要新增功能时,可以通过子类来实现扩展,而不需要改动原有的代码和代码框架,这符合软件设计的“开闭原则”,即对扩展开放,对修改封闭。
- 灵活性:所有子类实现的是同一套算法模型,在使用模板的地方,可以通过切换不同的子类来实不同的功能,这符合“里氏替换原则”。
- 维护性:由于模板模式的行为由父类控制,子类实现,这样的结构清晰,有利于维护。
缺点
- 复杂性:对于一些简单的任务,使用模板模式可能会引入不必要的复杂性。
- 继承的限制:模板模式基于继承,这意味着它可能不适用于所有的编程语言或框架,特别是那些不支持继承的语言或框架。
- 设计习惯:我们平常的设计习惯可能更偏向于直接实现功能,而不是先定义一个模板方法,这可能需要一定的适应时间。