模板方法
模板方法(Template Method)是一个比较简单的模式。它的主要思想是,定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。
因此,模板方法的核心在于定义一个“骨架”。我们还是举例说明。
假设我们开发了一个从数据库读取设置的类:
由于从数据库读取数据较慢,我们可以考虑把读取的设置缓存起来,这样下一次读取同样的key就不必再访问数据库了。但是怎么实现缓存,暂时没想好,但不妨碍我们先写出使用缓存的代码:
public final String getSetting(String key) {
// 先从缓存读取:
String value = lookupCache(key);
if (value == null) {
// 在缓存中未找到,从数据库读取:
value = readFromDatabase(key);
// 放入缓存:
putIntoCache(key, value);
} else {
System.out.println("[DEBUG] load from cache: " + key + " = " + value);
}
return value;
}
整个流程没有问题,但是,lookupCache(key)
和putIntoCache(key, value)
这两个方法还根本没实现,怎么编译通过?这个不要紧,我们声明抽象方法就可以:
假设我们希望用一个Map
做缓存,那么可以写一个LocalSetting
:
public class LocalSetting extends AbstractSetting {
private Map<String, String> cache = new HashMap<>();
protected String lookupCache(String key) {
return cache.get(key);
}
cache.put(key, value);
}
}
如果我们要使用Redis做缓存,那么可以再写一个RedisSetting
:
客户端代码使用本地缓存的代码这么写:
AbstractSetting setting1 = new LocalSetting();
System.out.println("test = " + setting1.getSetting("test"));
要改成Redis缓存,只需要把LocalSetting
替换为RedisSetting
:
可见,模板方法的核心思想是:父类定义骨架,子类实现某些细节。
Java标准库也有很多模板方法的应用。在集合类中,AbstractList
和AbstractQueuedSynchronizer
都定义了很多通用操作,子类只需要实现某些必要方法。
使用模板方法增加一个使用Guava Cache的子类。
从下载练习:模板方法练习 (推荐使用快速下载)
思考:能否将readFromDatabase()
作为模板方法,使得子类可以选择从数据库读取还是从文件读取。
再思考如果既可以扩展缓存,又可以扩展底层存储,会不会出现子类数量爆炸的情况?如何解决?