模板方法模式
大约 12 分钟
模板方法模式
什么是模板方法模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。
模板方法模式的核心思想是:
- 定义算法的骨架结构
- 将可变的部分延迟到子类实现
- 保持算法结构不变,允许子类重新定义特定步骤
- 通过钩子方法控制算法的某些步骤是否执行
为什么需要模板方法模式
在实际开发中,我们经常会遇到具有相同处理流程但某些步骤实现不同的情况。如果在每个子类中都重复实现相同的算法结构,会导致代码重复和维护困难。模板方法模式通过将公共的算法结构提取到父类中,将可变的部分留给子类实现,从而避免了代码重复,提高了代码的复用性和可维护性。
模板方法模式的结构
模板方法模式包含以下几个角色:
- 抽象类(AbstractClass):定义抽象的模板方法,实现通用的算法骨架
- 具体子类(ConcreteClass):实现抽象类中定义的抽象方法,完成特定步骤的实现
模板方法模式的实现
基本实现
// 抽象类
abstract class AbstractClass {
// 模板方法,定义算法骨架
public final void templateMethod() {
// 模板方法声明为final,防止子类重写
step1();
step2();
step3();
hookMethod();
}
// 基本方法,子类不能重写
private void step1() {
System.out.println("执行步骤1:公共操作");
}
// 抽象方法,子类必须实现
protected abstract void step2();
// 基本方法,子类不能重写
private void step3() {
System.out.println("执行步骤3:公共操作");
}
// 钩子方法,子类可以选择性重写
protected void hookMethod() {
// 默认实现为空
}
}
// 具体子类A
class ConcreteClassA extends AbstractClass {
@Override
protected void step2() {
System.out.println("具体类A执行步骤2:特定操作");
}
@Override
protected void hookMethod() {
System.out.println("具体类A执行钩子方法");
}
}
// 具体子类B
class ConcreteClassB extends AbstractClass {
@Override
protected void step2() {
System.out.println("具体类B执行步骤2:特定操作");
}
}
// 使用示例
public class TemplateMethodDemo {
public static void main(String[] args) {
System.out.println("=== 模板方法模式演示 ===");
System.out.println("1. 执行具体类A:");
AbstractClass classA = new ConcreteClassA();
classA.templateMethod();
System.out.println("\n2. 执行具体类B:");
AbstractClass classB = new ConcreteClassB();
classB.templateMethod();
}
}
改进的实现(带条件控制)
// 改进的抽象类
abstract class ImprovedAbstractClass {
// 模板方法
public final void templateMethod() {
step1();
if (needStep2()) {
step2();
}
step3();
if (needHookMethod()) {
hookMethod();
}
}
// 基本方法
private void step1() {
System.out.println("执行步骤1:初始化操作");
}
// 抽象方法
protected abstract void step2();
// 基本方法
private void step3() {
System.out.println("执行步骤3:清理操作");
}
// 钩子方法 - 控制是否执行step2
protected boolean needStep2() {
return true; // 默认执行
}
// 钩子方法 - 控制是否执行hookMethod
protected boolean needHookMethod() {
return false; // 默认不执行
}
// 钩子方法
protected void hookMethod() {
System.out.println("执行钩子方法");
}
}
// 改进的具体子类A
class ImprovedConcreteClassA extends ImprovedAbstractClass {
@Override
protected void step2() {
System.out.println("改进类A执行步骤2:数据处理");
}
@Override
protected boolean needHookMethod() {
return true; // 需要执行钩子方法
}
@Override
protected void hookMethod() {
System.out.println("改进类A执行特殊钩子方法");
}
}
// 改进的具体子类B
class ImprovedConcreteClassB extends ImprovedAbstractClass {
@Override
protected void step2() {
System.out.println("改进类B执行步骤2:业务逻辑");
}
@Override
protected boolean needStep2() {
return false; // 不需要执行步骤2
}
}
// 使用示例
public class ImprovedTemplateMethodDemo {
public static void main(String[] args) {
System.out.println("=== 改进的模板方法模式演示 ===");
System.out.println("1. 执行改进类A:");
ImprovedAbstractClass classA = new ImprovedConcreteClassA();
classA.templateMethod();
System.out.println("\n2. 执行改进类B:");
ImprovedAbstractClass classB = new ImprovedConcreteClassB();
classB.templateMethod();
}
}
模板方法模式的应用场景
- 框架设计:为框架用户提供扩展点
- 数据库操作:连接、执行、关闭的通用流程
- 文件处理:打开、读取、处理、关闭的通用流程
- Web请求处理:接收请求、验证、处理、返回响应的通用流程
- 游戏开发:游戏初始化、游戏循环、游戏结束的通用流程
- 测试框架:测试初始化、执行测试、清理的通用流程
模板方法模式的优缺点
优点
- 封装不变部分,扩展可变部分:将公共行为封装在父类中,子类只需实现可变部分
- 提取公共代码,便于维护:避免了代码重复,提高了代码复用性
- 行为由父类控制,子类实现:通过钩子方法实现反向控制
- 符合开闭原则:增加新的实现无须修改原有代码
缺点
- 每个不同的实现都需要一个子类:可能会增加系统中类的数量
- 违背里氏替换原则:子类可能会改变父类的行为
- 算法结构固定:模板方法定义了算法骨架,子类不能改变算法结构
模板方法模式与其他模式的比较
与策略模式的区别
- 模板方法模式:使用继承来实现算法的复用
- 策略模式:使用组合来实现算法的替换
与工厂方法模式的区别
- 模板方法模式:关注算法步骤的定义和执行
- 工厂方法模式:关注对象的创建
与建造者模式的区别
- 模板方法模式:定义算法的执行顺序
- 建造者模式:关注复杂对象的构建过程
实际项目中的应用
// 数据库操作模板示例
// 数据库操作抽象类
abstract class DatabaseTemplate {
// 模板方法
public final void executeQuery(String sql) {
Connection connection = null;
try {
// 连接数据库
connection = getConnection();
System.out.println("数据库连接成功");
// 执行查询
executeSQL(connection, sql);
// 处理结果
processResult();
} catch (Exception e) {
handleError(e);
} finally {
// 关闭连接
closeConnection(connection);
}
}
// 获取数据库连接 - 抽象方法
protected abstract Connection getConnection() throws Exception;
// 执行SQL - 基本方法
private void executeSQL(Connection connection, String sql) {
System.out.println("执行SQL: " + sql);
// 实际的SQL执行逻辑
}
// 处理结果 - 钩子方法
protected void processResult() {
System.out.println("处理查询结果");
}
// 错误处理 - 钩子方法
protected void handleError(Exception e) {
System.out.println("处理错误: " + e.getMessage());
}
// 关闭连接 - 基本方法
private void closeConnection(Connection connection) {
if (connection != null) {
try {
connection.close();
System.out.println("数据库连接已关闭");
} catch (Exception e) {
System.out.println("关闭连接时出错: " + e.getMessage());
}
}
}
}
// MySQL数据库操作实现
class MySQLDatabaseTemplate extends DatabaseTemplate {
private String url;
private String username;
private String password;
public MySQLDatabaseTemplate(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
@Override
protected Connection getConnection() throws Exception {
System.out.println("连接MySQL数据库: " + url);
// 实际的MySQL连接逻辑
return new MockConnection("MySQL");
}
@Override
protected void processResult() {
System.out.println("处理MySQL查询结果");
}
}
// Oracle数据库操作实现
class OracleDatabaseTemplate extends DatabaseTemplate {
private String url;
private String username;
private String password;
public OracleDatabaseTemplate(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
@Override
protected Connection getConnection() throws Exception {
System.out.println("连接Oracle数据库: " + url);
// 实际的Oracle连接逻辑
return new MockConnection("Oracle");
}
@Override
protected void handleError(Exception e) {
System.out.println("Oracle数据库错误处理: " + e.getMessage());
// Oracle特定的错误处理逻辑
}
}
// 模拟数据库连接类
class MockConnection {
private String databaseType;
public MockConnection(String databaseType) {
this.databaseType = databaseType;
}
public void close() throws Exception {
System.out.println("关闭" + databaseType + "连接");
}
}
// 使用示例
public class DatabaseTemplateDemo {
public static void main(String[] args) {
System.out.println("=== 数据库操作模板方法演示 ===");
// MySQL数据库操作
System.out.println("1. MySQL数据库操作:");
DatabaseTemplate mysqlTemplate = new MySQLDatabaseTemplate(
"jdbc:mysql://localhost:3306/test", "user", "password");
mysqlTemplate.executeQuery("SELECT * FROM users");
System.out.println("\n2. Oracle数据库操作:");
DatabaseTemplate oracleTemplate = new OracleDatabaseTemplate(
"jdbc:oracle:thin:@localhost:1521:xe", "user", "password");
oracleTemplate.executeQuery("SELECT * FROM employees");
}
}
// 游戏开发示例
// 游戏抽象类
abstract class Game {
// 模板方法
public final void play() {
// 初始化游戏
initialize();
// 开始游戏
startPlay();
// 游戏进行中
while (!endPlay()) {
playOneRound();
}
// 结束游戏
endGame();
}
// 初始化游戏 - 基本方法
private void initialize() {
System.out.println("游戏初始化");
}
// 开始游戏 - 抽象方法
protected abstract void startPlay();
// 游戏进行中 - 抽象方法
protected abstract void playOneRound();
// 是否结束游戏 - 钩子方法
protected boolean endPlay() {
return false; // 默认不结束
}
// 结束游戏 - 基本方法
private void endGame() {
System.out.println("游戏结束");
}
}
// 象棋游戏
class Chess extends Game {
private int round = 0;
private static final int MAX_ROUNDS = 3;
@Override
protected void startPlay() {
System.out.println("开始下象棋");
System.out.println("红方先行");
}
@Override
protected void playOneRound() {
round++;
System.out.println("第 " + round + " 回合");
System.out.println("红方走棋");
System.out.println("黑方走棋");
}
@Override
protected boolean endPlay() {
if (round >= MAX_ROUNDS) {
System.out.println("达到最大回合数");
return true;
}
return false;
}
}
// 西洋棋游戏
class ChessGame extends Game {
private int moveCount = 0;
private static final int MAX_MOVES = 5;
@Override
protected void startPlay() {
System.out.println("开始下西洋棋");
System.out.println("白方先行");
}
@Override
protected void playOneRound() {
moveCount++;
System.out.println("第 " + moveCount + " 步");
if (moveCount % 2 == 1) {
System.out.println("白方移动棋子");
} else {
System.out.println("黑方移动棋子");
}
}
@Override
protected boolean endPlay() {
if (moveCount >= MAX_MOVES) {
System.out.println("达到最大步数");
return true;
}
return false;
}
}
// 使用示例
public class GameTemplateDemo {
public static void main(String[] args) {
System.out.println("=== 游戏模板方法演示 ===");
System.out.println("1. 玩象棋:");
Game chess = new Chess();
chess.play();
System.out.println("\n2. 玩西洋棋:");
Game westernChess = new ChessGame();
westernChess.play();
}
}
Java中的模板方法模式应用
// Java Servlet中的模板方法模式
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
// 模拟HttpServlet的模板方法实现
abstract class MyHttpServlet extends HttpServlet {
// 模板方法 - service方法定义了处理HTTP请求的算法骨架
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求方法
String method = req.getMethod();
// 根据不同的HTTP方法调用相应的处理方法
if ("GET".equals(method)) {
doGet(req, resp);
} else if ("POST".equals(method)) {
doPost(req, resp);
} else if ("PUT".equals(method)) {
doPut(req, resp);
} else if ("DELETE".equals(method)) {
doDelete(req, resp);
} else {
// 不支持的方法
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
// 抽象方法 - 子类必须实现
protected abstract void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException;
// 抽象方法 - 子类必须实现
protected abstract void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException;
// 钩子方法 - 子类可以选择性重写
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
// 钩子方法 - 子类可以选择性重写
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
// 具体的Servlet实现
class MyServlet extends MyHttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println("处理GET请求");
System.out.println("执行GET请求处理逻辑");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println("处理POST请求");
System.out.println("执行POST请求处理逻辑");
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println("处理PUT请求");
System.out.println("执行PUT请求处理逻辑");
}
}
// 使用示例
public class ServletTemplateDemo {
public static void main(String[] args) {
System.out.println("=== Servlet模板方法演示 ===");
MyServlet servlet = new MyServlet();
// 模拟GET请求
System.out.println("1. 处理GET请求:");
// 在实际应用中,这些请求会由Web容器自动处理
System.out.println("GET请求处理由模板方法自动分发到doGet方法");
// 模拟POST请求
System.out.println("\n2. 处理POST请求:");
System.out.println("POST请求处理由模板方法自动分发到doPost方法");
}
}
// Java集合框架中的模板方法模式
import java.util.*;
// 模拟AbstractList的模板方法实现
abstract class MyAbstractList<E> {
// 模板方法 - add方法定义了添加元素的算法骨架
public boolean add(E e) {
add(size(), e); // 调用抽象方法
return true;
}
// 抽象方法 - 子类必须实现
public abstract void add(int index, E element);
// 抽象方法 - 子类必须实现
public abstract E get(int index);
// 抽象方法 - 子类必须实现
public abstract int size();
// 基本方法 - 使用抽象方法实现
public boolean isEmpty() {
return size() == 0;
}
// 基本方法 - 使用抽象方法实现
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 基本方法 - 使用抽象方法实现
public int indexOf(Object o) {
for (int i = 0; i < size(); i++) {
if (Objects.equals(o, get(i))) {
return i;
}
}
return -1;
}
}
// 具体实现 - ArrayList的简化版本
class MyArrayList<E> extends MyAbstractList<E> {
private Object[] elements;
private int size;
public MyArrayList() {
elements = new Object[10];
size = 0;
}
@Override
public void add(int index, E element) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException();
}
ensureCapacity();
// 移动元素
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
@Override
public E get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
return (E) elements[index];
}
@Override
public int size() {
return size;
}
private void ensureCapacity() {
if (size >= elements.length) {
Object[] newElements = new Object[elements.length * 2];
System.arraycopy(elements, 0, newElements, 0, size);
elements = newElements;
}
}
}
// 使用示例
public class CollectionTemplateDemo {
public static void main(String[] args) {
System.out.println("=== 集合框架模板方法演示 ===");
MyArrayList<String> list = new MyArrayList<>();
// 使用模板方法添加元素
System.out.println("1. 添加元素:");
list.add("元素1");
list.add("元素2");
list.add("元素3");
System.out.println("列表大小: " + list.size());
// 使用模板方法获取元素
System.out.println("\n2. 获取元素:");
for (int i = 0; i < list.size(); i++) {
System.out.println("索引 " + i + ": " + list.get(i));
}
// 使用继承的基本方法
System.out.println("\n3. 使用继承的方法:");
System.out.println("列表是否为空: " + list.isEmpty());
System.out.println("是否包含'元素2': " + list.contains("元素2"));
System.out.println("'元素3'的索引: " + list.indexOf("元素3"));
}
}
总结
模板方法模式是一种非常实用的行为型设计模式,它定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。
使用模板方法模式的关键点:
- 识别具有相同算法结构但某些步骤实现不同的场景
- 定义抽象类和模板方法
- 将可变的部分定义为抽象方法,由子类实现
- 使用钩子方法控制算法的某些步骤是否执行
模板方法模式的优点是封装不变部分,扩展可变部分,提取公共代码,便于维护,符合开闭原则。但需要注意的是,每个不同的实现都需要一个子类,可能会增加系统中类的数量。在现代Java开发中,模板方法模式广泛应用于框架设计、数据库操作、文件处理、Web请求处理等需要定义算法骨架的场景。Java标准库中的Servlet API、集合框架等都大量使用了模板方法模式。