12、模板方法模式
模板方法
见名思意, 模板方法就是先定义出来方法的模板,不给出具体的实现。将一些模板的步骤延迟到子类中实现,使得子类可以不改变一个算法的结构即可重构该算法的某些特定的步骤, 模板方法是一个种高层次的定义骨架, 底层实现细节的设计模式, 适用于流程固定。 但某些步骤可以替换的情况。
场景分析
需求: 一个本地缓存的需求, 但是具体的实现缓存方案没有给出,只是提供了一个方法的骨架,但是对于存入缓存、得到数据这两个核心方法没有给出具体的实现。此时便可使用模板方法来解决,为了编译通过, 把核心方法定义成抽像方法, 具体的实现交给子类来完成。
代码实现
1、算法骨架
为了编译时候能够通过,这里把核心步骤使用抽象方法来替换, 交由子类来实现具体的细节。
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;
}
private String readFromDatabase(String key) {
// TODO: 从数据库读取
return "data base";
}
/**
*存入缓存
* @param key
* @return
*/
protected abstract String lookupCache(String key);
/**
* 得到内容
* @param key
* @param value
*/
protected abstract void putIntoCache(String key, String value);
}
2、子类实现细节
子类直接继承上述算法, 实现其中定义的抽象方法。
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);
}
}
3、多种实现
public class RedisSetting extends AbstractSetting {
@Override
protected String lookupCache(String key) {
return "key";
}
@Override
protected void putIntoCache(String key, String value) {
}
}
测试
public class Test {
/**
* 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
*
* 模板方法是一种高层定义骨架,底层实现细节的设计模式,适用于流程固定,但某些步骤不确定或可替换的情况。
*/
public static void main(String[] args) {
AbstractSetting setting1 = new LocalSetting();
System.out.println("test = " + setting1.getSetting("test"));
System.out.println("test = " + setting1.getSetting("test"));
/**
* 可见,模板方法的核心思想是:父类定义骨架,子类实现某些细节。
*
* 为了防止子类重写父类的骨架方法,可以在父类中对骨架方法使用final。对于需要子类实现的抽象方法,一般声明为protected,使得这些方法对外部客户端不可见。
*
* Java标准库也有很多模板方法的应用。在集合类中,AbstractList和AbstractQueuedSynchronizer都定义了很多通用操作,子类只需要实现某些必要方法。
*
*
*/
AbstractSetting setting2 = new RedisSetting();
System.out.println("redis = " + setting2.getSetting("redis"));
System.out.println("redis = " + setting2.getSetting("redis"));
}
}
结果
[DEBUG] load from db: test = data base
test = data base
[DEBUG] load from cache: test = data base
test = data base
[DEBUG] load from cache: redis = key
redis = key
[DEBUG] load from cache: redis = key
redis = key
Process finished with exit code 0
关于抽象类的知识
- 抽象类不能实例化, 但是能够指向子类的引用
- 类中只要包含抽象方法就必须把类定义成抽象类
- 抽象类中可以同时包含抽象方法和带有具体实现的方法
- 抽象类中还可以包含属性字段
- 在模板方法中抽象方法可以定义为 protected 防止子类修改, 核心方法可以定义成final 不被子类修改。