人的知识就好比一个圆圈,圆圈里面是已知的,圆圈外面是未知的。你知道得越多,圆圈也就越大,你不知道的也就越多。

0%

设计模式--模板模式

定义

模板模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

角色

  • 抽象类(Abstract Class):实现了模板方法,定义了算法的骨架。
  • 具体类(Concrete Class):实现抽象类中的抽象方法,以完成完整的算法。

类图

Template UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public abstract class AbstractClass {

final void templateMethod() {
operation1();
operation2();
}

protected abstract void operation1();

protected abstract void operation2();
}

public class ConcreteClassA extends AbstractClass {

@Override
protected void operation1() {
System.out.println("operation1 of a");

}

@Override
protected void operation2() {
System.out.println("operation2 of a");
}
}

public class ConcreteClassB extends AbstractClass {

@Override
protected void operation1() {
System.out.println("operation1 of b");

}

@Override
protected void operation2() {
System.out.println("operation2 of b");
}
}

优缺点

优点

  • 封装必变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 行为由父类控制,子类实现

缺点

  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大

适用场景

  • 有多个子类共有的方法,且逻辑相同
  • 重要的、复杂的方法,都可以考虑作为模板方法

模式应用

Spring Security

TokenGranter UML

Java AQS抽象队列同步器

队列同步器(AbstractQueuedSynchronizer)是基于模板方法模式设计的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器聚合在自定义的同步组件中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。

Java线程池

AbstractExecutorService

实际应用

需求:很多单据需要提供参照服务,其模式是固定的:接受参数 -> 解析参数 -> 查询 -> 类型转转 -> 返回,
除了查询方法不同之外,其他各步骤实现都可以是相同的。
实现:提供模板类,各单据继承该模板类。
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class ReferService {

// 模板方法
public final <T> ReferPageResponse<T> queryReferPage(Map<String, Object> params, T clazz) {
Pageable pageable = buildPageable(params);
Page<T> page = queryForPage(pageable, params, clazz);
return buildResponse(page);
}

// 数据查询由子类实现
protected abstract <T> Page<T> queryForPage(Pageable pageable, Map<String, Object> params, T clazz);

private Pageable buildPageable(Map<String, Object> params) {
// TODO
return null;
}

private <T> ReferPageResponse<T> buildResponse(Page<T> page) {
// TODO
return null;
}
}

通过使用Java8 lambada函数,可以改进上面的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ReferUtils {

public final <T> ReferPageResponse<T> queryReferPage(Map<String, Object> params, BiFunction<Pageable, Map, Page<T>> function) {
Pageable pageable = buildPageable(params);
Page<T> page = function.apply(pageable, params);
return buildResponse(page);
}

private Pageable buildPageable(Map<String, Object> params) {
// TODO
return null;
}

private <T> ReferPageResponse<T> buildResponse(Page<T> page) {
// TODO
return null;
}
}

这样就不需要每个单据都创建子类了,而是改为传递数据查询函数。
简单业务中,如用到了行为型设计模式,基本都可以改为使用Java8 lambada函数,以避免类的增加。

小礼物走一走,来 Github 关注我