设计模式中,模板方法(Template Method)设计模式详解,Java代码示例说明。
模板方法(Template Method)设计模式详解
一、概念解释
模板方法(Template Method)模式:
在一个抽象类中定义一个操作的算法骨架,而将一些具体步骤延迟到子类中实现。子类在不改变算法整体结构的前提下,重新定义某些步骤的实现。
一句话总结:父类确定流程,子类决定细节。
二、特点
- 复用性:父类定义了通用的算法骨架,避免子类重复实现流程逻辑。
- 扩展性:通过新增子类实现不同的步骤细节,扩展灵活。
- 封装性:核心框架固定,细节可变,符合开闭原则。
- 安全性:骨架方法通常用
final修饰,防止子类意外修改整体逻辑。
三、适用场景
- 多个子类有相同的流程结构,但某些步骤细节不同。
- 希望控制子类扩展点,但不希望子类改变算法主流程。
- 需要在一个框架内实现不同的策略或功能扩展。
典型应用:
- Java 集合框架中的
AbstractList、AbstractMap等基类。 - Java 并发包中的
AbstractQueuedSynchronizer (AQS)。
四、示例代码(补充完整)
抽象模板类
public abstract class AbstractSetting {
public final String getSetting(String key) {
// 骨架逻辑
String value = lookupCache(key);
if (value == null) {
value = readFromDatabase(key);
System.out.println("[DEBUG] load from db: " + key + " = " + value);
putIntoCache(key, value);
} else {
System.out.println("[DEBUG] load from cache: " + key + " = " + value);
}
return value;
}
// 缓存查找(延迟到子类实现)
protected abstract String lookupCache(String key);
// 缓存写入(延迟到子类实现)
protected abstract void putIntoCache(String key, String value);
// 数据读取(父类统一实现,也可考虑抽象化)
protected String readFromDatabase(String key) {
return "ValueOf(" + key + ")";
}
}
本地缓存实现
import java.util.HashMap;
import java.util.Map;
public class LocalSetting extends AbstractSetting {
private Map<String, String> cache = new HashMap<>();
@Override
protected String lookupCache(String key) {
return cache.get(key);
}
@Override
protected void putIntoCache(String key, String value) {
cache.put(key, value);
}
}
Redis缓存实现(伪代码)
// 注意:此处仅为演示,需引入 Lettuce 或 Jedis 客户端
public class RedisSetting extends AbstractSetting {
// 模拟 Redis 存储
private Map<String, String> redis = new HashMap<>();
@Override
protected String lookupCache(String key) {
return redis.get(key);
}
@Override
protected void putIntoCache(String key, String value) {
redis.put(key, value);
}
}
五、练习题实现
练习1:使用 Guava Cache
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public class GuavaSetting extends AbstractSetting {
private Cache<String, String> cache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES) // 设置过期时间
.maximumSize(100) // 最大缓存条目数
.build();
@Override
protected String lookupCache(String key) {
return cache.getIfPresent(key);
}
@Override
protected void putIntoCache(String key, String value) {
cache.put(key, value);
}
}
客户端测试
public class Main {
public static void main(String[] args) {
AbstractSetting setting = new GuavaSetting();
System.out.println("test = " + setting.getSetting("test"));
System.out.println("test = " + setting.getSetting("test"));
}
}
六、练习思考题回答
1. 能否将 readFromDatabase() 作为模板方法?
可以。如果希望支持“数据库 / 文件 / 网络”三种不同的存储方式,可以把 readFromDatabase() 改成抽象方法,让子类去决定从哪里读数据。
2. 如果既可以扩展缓存,又可以扩展底层存储,会不会出现子类数量爆炸?
会。例如:
- 本地缓存 + 数据库
- 本地缓存 + 文件
- Redis缓存 + 数据库
- Redis缓存 + 文件
……
组合一多,子类就会呈指数级增长。
3. 如何解决子类数量爆炸问题?
- 使用 组合优于继承 的思想,把“缓存策略”和“存储策略”拆分成两个独立接口:
interface Cache {
String lookup(String key);
void put(String key, String value);
}
interface Storage {
String read(String key);
}
Setting类通过组合这两个接口来完成逻辑。这样就可以自由组合,而不是写很多子类。- 这种改造思路其实接近 策略模式 (Strategy Pattern),可灵活组合缓存策略和存储策略。

浙公网安备 33010602011771号