定义
- 定义了一个行为的骨架,并允许子类为一个或多个步骤提供实现。
- 模版方法使得子类可以在不改变行为结构的情况下,重新定义某些步骤。
适用场景
- 一次性实现行为的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为被提取出来并集中到一个父类中,从而避免代码重复。
优点
- 提高复用性。
- 提高扩展性。
- 符合开闭原则。
缺点
- 类书目的增加。
- 增加了系统实现的复杂度。
- 继承关系自身缺点,如果父类添加新的抽象,子类都需要重新实现一遍。
代码
模版方法非常简单,相信很多小伙伴已经在使用了。我们来举一个比较通常的例子。
我们经常在业务中需要读取 excel,csv,txt,json等格式的数据,并且将他们清洗入库。
因此我们可以抽象一个模版
文件处理模版:
- 读取文件
- 清洗数据
- 数据入库
- 数据导出(钩子方法来判断)
注意:这里只是一个概念抽象,具体的实现请不要纠结
文件处理模版类(FileDataHandler
)
/**
* 抽象父类
*/
public abstract class FileDataHandler {
protected final void handleData() {
readData();
proccessData();
intoDataBase();
if(needExport()) {
exportData();
}
}
/**
* 读取数据
*/
final void readData() {
System.out.println("----读取数据----");
}
/**
* 清洗数据
*/
final void proccessData() {
System.out.println("----清洗数据-----");
}
/**
* 数据入库
*/
final void intoDataBase() {
System.out.println("----数据入库-----");
}
/**
* 导出数据
*/
final void exportData() {
System.out.println("----导出数据-----");
}
/**
* 是否导出数据
* @return
*/
protected boolean needExport() {
return false;
}
/**
* 获取数据源
*/
protected abstract void openSource();
}
这里看到 我们抽取固定的行为 已经定义为final
修饰的方法,这样子类是无法重写的,保证了行为的一致性。
我们还添加了钩子方法needExport()
来选择行导出数据。
同时定义了抽象方法打开数据源,由于我们打开文件的源不同,但是这又是读取文件的必要操作,因此我们给他定义为一个抽象方法,这样强制子类去执行,防止子类没有初始化源的情况。
处理Excel (ExcelDataHandler
)
/**
* Excel文件处理类
*/
public class ExcelDataHandler extends FileDataHandler {
@Override
protected void openSource() {
System.out.println("打开Excel文件");
}
}
正常处理,没有导出操作。
处理JSON (JsonDataHandler
)
/**
* Json文件处理类
*/
public class JsonDataHandler extends FileDataHandler {
@Override
protected void openSource() {
System.out.println("打开json文件");
}
/**
* 激活钩子方法 导出
* @return
*/
@Override
protected boolean needExport() {
return true;
}
}
这里打开了钩子,开启数据导出。
UML类图

测试
public class TemplateBootstrap {
public static void main(String[] args) {
FileDataHandler excel = new ExcelDataHandler();
excel.openSource();
excel.handleData();
System.out.println("----------------------------------");
FileDataHandler json = new JsonDataHandler();
json.openSource();
json.handleData();
}
}

其他开源使用
JDK
AbstractList
:在AbstractList
中,抽象出来了List
下公共的行为方法。我们看一下源码就清楚了。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
/**
* Sole constructor. (For invocation by subclass constructors, typically
* implicit.)
*/
protected AbstractList() {
}
/**
* Appends the specified element to the end of this list (optional
* operation).
*
* <p>Lists that support this operation may place limitations on what
* elements may be added to this list. In particular, some
* lists will refuse to add null elements, and others will impose
* restrictions on the type of elements that may be added. List
* classes should clearly specify in their documentation any restrictions
* on what elements may be added.
*
* <p>This implementation calls {@code add(size(), e)}.
*
* <p>Note that this implementation throws an
* {@code UnsupportedOperationException} unless
* {@link #add(int, Object) add(int, E)} is overridden.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
* @throws UnsupportedOperationException if the {@code add} operation
* is not supported by this list
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this list
*/
public boolean add(E e) {
add(size(), e);
return true;
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
abstract public E get(int index);
相信小伙伴一看就了解了。
SpringBoot
在Spring 中 有一个内容协商类 AbstractNamedValueMethodArgumentResolver
也将针对请求中的参数解析行为抽象成了一个父类。
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Nullable
private final ConfigurableBeanFactory configurableBeanFactory;
@Nullable
private final BeanExpressionContext expressionContext;
//省略部分代码
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
//....
小结
模版方法模式很简单,但是确实体现了抽象意义。
网友评论