前面文章由抽奖活动赠送不同的奖励从而引出了策略模式,定义一个策略接口,不同的奖励发放实现不同的策略。即使后续新增加奖励种类,只要重新实现一个策略即可,符合了设计模式中的开闭原则-对扩展开放对修改关闭。我在一次订单活动功能开发中使用到了策略模式,在开发的过程中发现,订单符合活动条件的判断、发放奖励给用户,可以提升到父类实现,具体的条件判断逻辑可以延迟到子类去实现,从而引出本篇文章的主题:模板模式。
模板模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行,这种类型的设计模式属于行为型模式。模板模式中涉及到在父类实现算法骨架,具体步骤在子类实现,所以必须要有抽象类(Java8中的接口的 default 方法貌似也可以实现)。
介绍
- 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
- 何时使用:有一些通用的方法。
- 如何解决:将这些通用算法抽象出来。
- 关键代码:在抽象类实现,其他步骤延迟到子类实现。
应用实例:
- JDK中 ReentrantLock中公平锁和非公平锁的实现
- Spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点:
- 封装不变部分,扩展可变部分。
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现。
缺点:
- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景:
- 有多个子类共有的方法,且逻辑相同。
- 重要的、复杂的方法,可以考虑作为模板方法。
注意事项:
为防止恶意操作,一般模板方法都加上 final 关键词。
本文示例UML图
代码结构图
示例代码
1、策略接口,定义了一个活动策略需要实现的方法
1 |
|
2、活动抽象类包含订单规则是否符合的判断以及符合规则之后发送奖励的算法骨架。
1 | package com.ydstudio.flashsale.module.pattern.service; |
3、 活动策略的实现类
会籍订单策略实现类
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
40
41
42
43
44
45
46
47package com.ydstudio.flashsale.module.pattern.service.impl;
import com.ydstudio.flashsale.common.Result;
import com.ydstudio.flashsale.module.pattern.dto.ActiveOrderDto;
import com.ydstudio.flashsale.module.pattern.service.AbstractActiveHandle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
*
* 会籍订单的处理
*
* @author Sam
* @date 2020/11/26
* @since 1.7.3
*/
4j
public class LevelOrderActiveHandle extends AbstractActiveHandle {
public void otherMethod() {
System.out.println("会籍订单的实现");
}
public String getCategory() {
return "2";
}
public String getCategoryDetail() {
return "2";
}
public Result checkOrder(ActiveOrderDto temporaryOrderDto) {
Random random = new Random();
int i = random.nextInt(4);
if (i >=2){
return new Result();
}
return new Result("不符合会籍订单活动条件","不符合会籍订单活动条件");
}
}酒店订单活动策略实现类
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
40
41
42
43
44
45
46
47
48package com.ydstudio.flashsale.module.pattern.service.impl;
import com.ydstudio.flashsale.common.Result;
import com.ydstudio.flashsale.module.pattern.dto.ActiveOrderDto;
import com.ydstudio.flashsale.module.pattern.service.AbstractActiveHandle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
*
* 酒店订单处理
*
* @author Sam
* @date 2020/11/26
* @since 1.7.3
*/
4j
public class OrderActiveHandle extends AbstractActiveHandle {
public void otherMethod() {
System.out.println("酒店订单的实现");
}
public String getCategory() {
return "1";
}
public String getCategoryDetail() {
return "1";
}
public Result checkOrder(ActiveOrderDto temporaryOrderDto) {
Random random = new Random();
int i = random.nextInt(4);
if (i<=2){
return new Result();
}
return new Result("不符合酒店订单活动条件","不符合酒店订单活动条件");
}
}
4 、统一入口提供一个Context 给外部使用
调用方直接使用@Autowired 注入ActiveHandleContext即可使用。
1 | package com.ydstudio.flashsale.module.pattern.service; |
父类AbstractActiveHandle实现活动规则判断、发放奖励的算法骨架,子类实现具体的规则判断,这样就实现了代码的优化和公用。