策略模式

问题说明

当前项目中,订单的状态比较多,不同状态下取消订单的逻辑是不同的;而且用户不同,可操作的内容也不同

  • 普通用户:可取消待支付、派单中、待服务的订单
  • 运营人员:可取消派单中、待服务、服务中、完成的订单
whiteboard_exported_image (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
public void cancel(OrderCancelDTO orderCancelDTO) {

Orders orders = getById(orderCancelDTO.getId());//查询订单信息
Integer ordersStatus = orders.getOrdersStatus();//订单状态
CurrentUserInfo currentUserInfo = UserContext.currentUser();//获取当前用户
Integer userType = currentUserInfo.getUserType();//用户类型

if(UserType.C_USER==userType){//普通用户取消订单
if(OrderStatusEnum.NO_PAY.getStatus()==ordersStatus){ //订单状态为待支付
//...
}else if(OrderStatusEnum.DISPATCHING.getStatus()==ordersStatus){ //订单状态为派单中
//...
}else if(OrderStatusEnum.NO_SERVE.getStatus()==ordersStatus){ //订单状态为待服务
//...
}else{
throw new CommonException("当前订单状态不支持取消");
}
}else if(UserType.OPERATION==userType){//运营人员取消订单
if(OrderStatusEnum.DISPATCHING.getStatus()==ordersStatus) { //订单状态为派单中
//...
}else if(OrderStatusEnum.NO_SERVE.getStatus()==ordersStatus){//订单状态为待服务
//...
}else if(OrderStatusEnum.SERVING.getStatus()==ordersStatus){//订单状态为服务中
//...
}else if(OrderStatusEnum.FINISHED.getStatus()==ordersStatus){//订单状态为已完成
//...
}else{
throw new CommonException("当前订单状态不支持取消");
}
}
}

这种代码虽然可以实现功能,但是可读性和扩展性都很差,我们可以使用策略模式进行改造

策略模式

使用场景:针对一个业务,有多个不同的执行分支,需要根据不同的条件,切换到不同的分支上去

好处:解决使用条件语句(如if…else)导致的复杂性和扩展性问题

策略模式需要开发下面的接口和类:

  • 策略接口:定义策略方法
  • 策略实现类:需要实现策略接口,并重写接口中声明的策略方法
  • 上下文环境类:需要维护所有策略对象,然后根据不同的条件,调用不同的策略对象
9c5aff57-8b8e-4dbf-b596-26bc6114310a

下面以一个**支付方式**选择的场景为例子来讲解一下,需求如下:

  1. 当前项目支持微信和阿里两种支付方式
  2. 用户可以自行选择一种支付方式
  3. 根据用户选择,执行对应的支付代码

1)定义策略接口

创建PayStrategy接口

1
2
3
4
5
6
7
8
9
10
package com.jzo2o.orders.manager.service.strategy;

/**
* 支付策略接口
*/
public interface PayStrategy {

//策略方法
void pay();
}

2)定义微信支付策略类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.jzo2o.orders.manager.service.strategy.impl;

import com.jzo2o.orders.manager.service.strategy.PayStrategy;
import org.springframework.stereotype.Component;

/**
* 微信支付策略实现类
*/
@Component("wxPay")
public class WxPayStrategy implements PayStrategy {
@Override
public void pay() {
System.out.println("使用微信进行支付");
}
}

3)定义阿里支付策略类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.jzo2o.orders.manager.service.strategy.impl;

import com.jzo2o.orders.manager.service.strategy.PayStrategy;
import org.springframework.stereotype.Component;

/**
* 阿里支付策略实现类
*/
@Component("aliPay")
public class AliPayStrategy implements PayStrategy {
@Override
public void pay() {
System.out.println("使用阿里进行支付");
}
}

4)定义上下文环境类

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
package com.jzo2o.orders.manager.service.strategy;

import cn.hutool.extra.spring.SpringUtil;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;

/**
* 上下文环境类(策略管理类)
*/
@Component
public class PayStrategyManager {
Map<String, PayStrategy> payStrategyMap;

//维护所有策略对象
@PostConstruct //此注解标注的方法会在当前对象PayStrategyManager创建之后自动执行
public void init() {
//key: 当前对象在容器中的id
//value: 当前对象
payStrategyMap = SpringUtil.getBeansOfType(PayStrategy.class);
}

//根据用户需求, 执行指定的策略对象
public void pay(String key) {
PayStrategy payStrategy = payStrategyMap.get(key);
if (payStrategy == null) {
throw new RuntimeException("暂不支持当前支付方式");
}
payStrategy.pay();
}
}

5)测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.jzo2o.orders.manager.service.strategy;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
* 测试支付方式的选择
*/
@SpringBootTest
public class PayTest {

@Autowired
private PayStrategyManager payStrategyManager;

@Test
public void test(){
//测试微信支付
payStrategyManager.pay("wxPay");
//测试阿里支付
payStrategyManager.pay("aliPay");
}
}