设计模式中,模板方法(Template Method)设计模式详解,Java代码示例说明。

模板方法(Template Method)设计模式详解

一、概念解释

模板方法(Template Method)模式:
在一个抽象类中定义一个操作的算法骨架,而将一些具体步骤延迟到子类中实现。子类在不改变算法整体结构的前提下,重新定义某些步骤的实现。

一句话总结:父类确定流程,子类决定细节。


二、特点

  • 复用性:父类定义了通用的算法骨架,避免子类重复实现流程逻辑。
  • 扩展性:通过新增子类实现不同的步骤细节,扩展灵活。
  • 封装性:核心框架固定,细节可变,符合开闭原则。
  • 安全性:骨架方法通常用 final 修饰,防止子类意外修改整体逻辑。

三、适用场景

  1. 多个子类有相同的流程结构,但某些步骤细节不同。
  2. 希望控制子类扩展点,但不希望子类改变算法主流程。
  3. 需要在一个框架内实现不同的策略或功能扩展

典型应用:

  • Java 集合框架中的 AbstractListAbstractMap 等基类。
  • 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),可灵活组合缓存策略和存储策略。

posted @ 2025-09-09 17:35  AlphaGeek  阅读(9)  评论(0)    收藏  举报