模板方法

    模板方法(Template Method)是一个比较简单的模式。它的主要思想是,定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。

    因此,模板方法的核心在于定义一个“骨架”。我们还是举例说明。

    假设我们开发了一个从数据库读取设置的类:

    由于从数据库读取数据较慢,我们可以考虑把读取的设置缓存起来,这样下一次读取同样的key就不必再访问数据库了。但是怎么实现缓存,暂时没想好,但不妨碍我们先写出使用缓存的代码:

    1. public final String getSetting(String key) {
    2. // 先从缓存读取:
    3. String value = lookupCache(key);
    4. if (value == null) {
    5. // 在缓存中未找到,从数据库读取:
    6. value = readFromDatabase(key);
    7. // 放入缓存:
    8. putIntoCache(key, value);
    9. } else {
    10. System.out.println("[DEBUG] load from cache: " + key + " = " + value);
    11. }
    12. return value;
    13. }

    整个流程没有问题,但是,lookupCache(key)putIntoCache(key, value)这两个方法还根本没实现,怎么编译通过?这个不要紧,我们声明抽象方法就可以:

    假设我们希望用一个Map做缓存,那么可以写一个LocalSetting

    1. public class LocalSetting extends AbstractSetting {
    2. private Map<String, String> cache = new HashMap<>();
    3. protected String lookupCache(String key) {
    4. return cache.get(key);
    5. }
    6. cache.put(key, value);
    7. }
    8. }

    如果我们要使用Redis做缓存,那么可以再写一个RedisSetting

    客户端代码使用本地缓存的代码这么写:

    1. AbstractSetting setting1 = new LocalSetting();
    2. System.out.println("test = " + setting1.getSetting("test"));

    要改成Redis缓存,只需要把LocalSetting替换为RedisSetting

    可见,模板方法的核心思想是:父类定义骨架,子类实现某些细节。

    Java标准库也有很多模板方法的应用。在集合类中,AbstractListAbstractQueuedSynchronizer都定义了很多通用操作,子类只需要实现某些必要方法。

    使用模板方法增加一个使用Guava Cache的子类。

    从下载练习:模板方法练习 (推荐使用快速下载)

    思考:能否将readFromDatabase()作为模板方法,使得子类可以选择从数据库读取还是从文件读取。

    再思考如果既可以扩展缓存,又可以扩展底层存储,会不会出现子类数量爆炸的情况?如何解决?

    模板方法 - 图1