模板方法模式 java

1. 模板方法模式概述

       在现实生活中,很多事情都包含几个实现步骤,例如请客吃饭,无论吃什么,一般都包含点单、吃东西、买单等几个步骤,通常情况下这几个步骤的次序是:点单 –> 吃东西 –> 买单。在这三个步骤中,点单和买单大同小异,最大的区别在于第二步——吃什么?吃面条和吃满汉全席可大不相同,如图1所示:

图1 请客吃饭示意图

        在软件开发中,有时也会遇到类似的情况,某个方法的实现需要多个步骤(类似“请客”),其中有些步骤是固定的(类似“点单”和“买单”),而有些步骤并不固定,存在可变性(类似“吃东西”)。为了提高代码的复用性和系统的灵活性,可以使用一种称之为模板方法模式的设计模式来对这类情况进行设计,在模板方法模式中,将实现功能的每一个步骤所对应的方法称为基本方法(例如“点单”、“吃东西”和“买单”),而调用这些基本方法同时定义基本方法的执行次序的方法称为模板方法(例如“请客”)。在模板方法模式中,可以将相同的代码放在父类中,例如将模板方法“请客”以及基本方法“点单”和“买单”的实现放在父类中,而对于基本方法“吃东西”,在父类中只做一个声明,将其具体实现放在不同的子类中,在一个子类中提供“吃面条”的实现,而另一个子类提供“吃满汉全席”的实现。通过使用模板方法模式,一方面提高了代码的复用性,另一方面还可以利用面向对象的多态性,在运行时选择一种具体子类,实现完整的“请客”方法,提高系统的灵活性和可扩展性。

       模板方法模式定义如下:

模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

 

Template Method Pattern:  Define the skeleton of an algorithm in an  operation, deferring some steps to subclasses. Template Method lets  subclasses redefine certain steps of an algorithm without changing the  algorithm’s structure.

       模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式

       模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成。

 

2. 模板方法模式结构与实现

2.1 模式结构

      模板方法模式结构比较简单,其核心是抽象类和其中的模板方法的设计,其结构如图2所示:

图2 模板方法模式结构图

      由图2可知,模板方法模式包含如下两个角色:

       (1) AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。

       (2) ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

 

2.2 模式实现

       在实现模板方法模式时,开发抽象类的软件设计师和开发具体子类的软件设计师之间可以进行协作。一个设计师负责给出一个算法的轮廓和框架,另一些设计师则负责给出这个算法的各个逻辑步骤。实现这些具体逻辑步骤的方法即为基本方法,而将这些基本方法汇总起来的方法即为模板方法,模板方法模式的名字也因此而来。下面将详细介绍模板方法和基本方法:

       1. 模板方法

       一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。

         2. 基本方法

       基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

       (1) 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。在C#语言里一个抽象方法以abstract关键字标识。

       (2) 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

       (3) 钩子方法:一个钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展。通常在父类中给出的实现是一个空实现(可使用virtual关键字将其定义为虚函数),并以该空实现作为方法的默认实现,当然钩子方法也可以提供一个非空的默认实现。

       在模板方法模式中,钩子方法有两类:第一类钩子方法可以与一些具体步骤“挂钩”,以实现在不同条件下执行模板方法中的不同步骤,这类钩子方法的返回类型通常是bool类型的,这类方法名一般为IsXXX(),用于对某个条件进行判断,如果条件满足则执行某一步骤,否则将不执行,如下代码片段所示:

[csharp] view plain copy
print ?
  1. ……  
  2. //模板方法  
  3. public void TemplateMethod()   
  4. {  
  5. Open();  
  6. Display();  
  7. //通过钩子方法来确定某步骤是否执行  
  8. if (IsPrint())   
  9. {  
  10.     Print();  
  11. }  
  12. }  
  13.   
  14. //钩子方法  
  15. public bool IsPrint()  
  16. {  
  17.     return true;  
  18. }  
  19. ……  
……
//模板方法
public void TemplateMethod() 
{
Open();
Display();
//通过钩子方法来确定某步骤是否执行
if (IsPrint()) 
{
    Print();
}
}

//钩子方法
public bool IsPrint()
{
    return true;
}
……

        在代码中IsPrint()方法即是钩子方法,它可以决定Print()方法是否执行,一般情况下,钩子方法的返回值为true,如果不希望某方法执行,可以在其子类中覆盖钩子方法,将其返回值改为false即可,这种类型的钩子方法可以控制方法的执行,对一个算法进行约束。

       还有一类钩子方法就是实现体为空的具体方法,子类可以根据需要覆盖或者继承这些钩子方法,与抽象方法相比,这类钩子方法的好处在于子类如果没有覆盖父类中定义的钩子方法,编译可以正常通过,但是如果没有覆盖父类中声明的抽象方法,编译将报错。

       在模板方法模式中,抽象类的典型代码如下:

[csharp] view plain copy
print ?
  1. abstract class AbstractClass   
  2. {  
  3. //模板方法  
  4. public void TemplateMethod()   
  5. {  
  6.         PrimitiveOperation1();  
  7.         PrimitiveOperation2();  
  8.         PrimitiveOperation3();  
  9. }  
  10.   
  11. //基本方法—具体方法  
  12. public void PrimitiveOperation1()   
  13. {  
  14.     //实现代码  
  15. }  
  16.   
  17. //基本方法—抽象方法  
  18.     public abstract void PrimitiveOperation2();      
  19.   
  20. //基本方法—钩子方法  
  21. public virtual void PrimitiveOperation3()     
  22. {  }  
  23. }  
abstract class AbstractClass 
{
//模板方法
public void TemplateMethod() 
{
        PrimitiveOperation1();
        PrimitiveOperation2();
        PrimitiveOperation3();
}

//基本方法—具体方法
public void PrimitiveOperation1() 
{
    //实现代码
}

//基本方法—抽象方法
    public abstract void PrimitiveOperation2();    

//基本方法—钩子方法
public virtual void PrimitiveOperation3()   
{  }
}

        在抽象类中,模板方法TemplateMethod()定义了算法的框架,在模板方法中调用基本方法以实现完整的算法,每一个基本方法如PrimitiveOperation1()、PrimitiveOperation2()等均实现了算法的一部分,对于所有子类都相同的基本方法可在父类提供具体实现,例如PrimitiveOperation1(),否则在父类声明为抽象方法或钩子方法,由不同的子类提供不同的实现,例如PrimitiveOperation2()和PrimitiveOperation3()。

        可在抽象类的子类中提供抽象步骤的实现,也可覆盖父类中已经实现的具体方法,具体子类的典型代码如下:

[csharp] view plain copy
print ?
  1. class ConcreteClass : AbstractClass   
  2. {  
  3. public override void PrimitiveOperation2()   
  4. {  
  5.     //实现代码  
  6. }  
  7.   
  8. public override void PrimitiveOperation3()   
  9. {  
  10.     //实现代码  
  11. }  
  12. }  
class ConcreteClass : AbstractClass 
{
public override void PrimitiveOperation2() 
{
    //实现代码
}

public override void PrimitiveOperation3() 
{
    //实现代码
}
}

       在模板方法模式中,由于面向对象的多态性,子类对象在运行时将覆盖父类对象,子类中定义的方法也将覆盖父类中定义的方法,因此程序在运行时,具体子类的基本方法将覆盖父类中定义的基本方法,子类的钩子方法也将覆盖父类的钩子方法,从而可以通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制。

  【作者:刘伟   http://blog.youkuaiyun.com/lovelion

### 模板方法模式的概述 模板方法模式是一种行为型设计模式,主要用于定义一个操作中的算法骨架,并将一些具体步骤的实现延迟到子类中完成。这种模式允许子类在不修改算法结构的情况下重新定义某些特定步骤的行为[^2]。 --- ### 模板方法模式的核心概念 1. **抽象类**:定义了算法的整体框架以及部分公共逻辑。 2. **钩子方法(Hook Methods)**:提供给子类可选的扩展点,用于控制算法的具体行为。 3. **模板方法**:由父类定义并封装整个算法流程,不可被子类覆盖。 4. **具体子类**:继承自抽象类,负责实现未定义的部分或重写默认实现的方法。 通过这种方式,模板方法模式能够有效提升代码复用性和灵活性,同时保持良好的结构化设计[^5]。 --- ### Java 中的实现与示例 以下是基于模板方法模式的一个典型 Java 示例: #### 场景描述 假设我们需要创建一个咖啡店的应用程序,其中不同的饮品制作过程具有相同的总体流程(比如煮水、冲泡、加入调料等),但每种饮品的具体细节可能有所不同。可以通过模板方法模式来解决这一需求。 #### 代码实现 ```java // 抽象类 AbstractBeverage 定义整体流程 abstract class AbstractBeverage { // 模板方法 (final 防止子类覆写) public final void prepare() { boilWater(); // 步骤一:煮水 brew(); // 步骤二:冲泡饮料 pourInCup(); // 步骤三:倒入杯子 addCondiments(); // 步骤四:添加调料 } private void boilWater() { System.out.println("Boiling water..."); } abstract void brew(); // 子类需要实现的冲泡方式 private void pourInCup() { System.out.println("Pouring into cup..."); } // 可选的钩子方法,默认为空实现 protected void addCondiments() {} } // 具体子类 Coffee 继承并实现细节 class Coffee extends AbstractBeverage { @Override void brew() { System.out.println("Dripping coffee through filter..."); } @Override protected void addCondiments() { // 覆盖钩子方法 System.out.println("Adding sugar and milk..."); } } // 具体子类 Tea 继承并实现细节 class Tea extends AbstractBeverage { @Override void brew() { System.out.println("Steeping the tea..."); } @Override protected void addCondiments() {} // 不加调料 } ``` #### 测试运行 ```java public class TemplateMethodDemo { public static void main(String[] args) { AbstractBeverage coffee = new Coffee(); coffee.prepare(); System.out.println("----------------"); AbstractBeverage tea = new Tea(); tea.prepare(); } } ``` #### 输出结果 ``` Boiling water... Dripping coffee through filter... Pouring into cup... Adding sugar and milk... ---------------- Boiling water... Steeping the tea... Pouring into cup... ``` 上述例子展示了如何利用模板方法模式统一管理不同类型的饮品准备流程,同时保留足够的自由度供子类定制自己的独特之处[^4]。 --- ### JDBC 操作中的应用案例 除了简单的饮品制备场景外,模板方法模式还广泛应用于复杂的企业级开发领域。例如,在数据库访问层中,可以使用此模式简化重复性的 CRUD 操作逻辑。下面是一个基于 JDBC 的简单示例[^3]: ```java import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; // 抽象基类 JdbcTemplate abstract class JdbcTemplate<T> { Connection connection; // 数据库连接对象 T executeQuery(String sql, Object... params) throws Exception { try ( PreparedStatement stmt = this.connection.prepareStatement(sql); ResultSet rs = stmt.executeQuery() ) { bindParameters(stmt, params); // 设置参数 return processResult(rs); // 处理查询结果 } finally { closeConnection(); // 关闭资源 } } abstract T processResult(ResultSet rs) throws Exception; // 结果集处理交由子类实现 private void bindParameters(PreparedStatement stmt, Object[] params) throws Exception { if (params != null && params.length > 0) { for (int i = 0; i < params.length; i++) { stmt.setObject(i + 1, params[i]); } } } private void closeConnection() { if (this.connection != null) { try { this.connection.close(); } catch (Exception e) { e.printStackTrace(); } } } } // 子类 UserJdbc 查询用户数据 class UserJdbc extends JdbcTemplate<User> { @Override User processResult(ResultSet rs) throws Exception { if (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); return new User(name, age); } return null; } } class User { String name; int age; public User(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "User{name='" + name + "', age=" + age + "}"; } } ``` 在这个例子中,`JdbcTemplate` 提供了一套标准的数据访问接口,而 `UserJdbc` 则专注于具体的业务逻辑实现。这样的分离不仅提高了代码的可读性,也增强了系统的可维护性。 --- ### 总结 模板方法模式适用于那些存在固定流程但又有一定差异化的应用场景。借助于该模式,开发者可以在高层面上定义好通用规则,而在低层次上灵活调整局部特性,从而达到更高的代码复用率和更好的模块划分效果[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值