策略模式

以下是本人在学习设计模式的过程中的一些笔记和代码.下面的内容主要是在看完了B站视频: 策略模式 之后的笔记总结.
策略模式一般用来优化if-else结构,减少if-else层级,是的代码的可读性增加,那么策略模式究竟是怎么样优化if-else结构的呢?下面展示的是一个使用策略模式优化一段if-else结构,一步一步思考逻辑.

Version1

先看下面的一段代码:

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
package org.lin.strategicdesignpattern.version1.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author Xiaobin
* @since 2025/05/26 22:10
*/
@RestController
@RequestMapping("/customer/v1")
public class CustomerControllerV1 {


@GetMapping("/{recharge}")
public String customer(@PathVariable(value = "recharge") Integer recharge) {
if (recharge >0 && recharge <= 100) {
// ....一些其他的逻辑
return "普通玩家客服";
}
if (recharge > 100 && recharge <= 10000) {
// ....一些其他的逻辑
return "VIP玩家客服";
}
if (recharge > 10_000 && recharge <= 100_000) {
// ....一些其他的逻辑
return "VVIP玩家客服";
}
if (recharge > 100_000 && recharge <= 1_000_000) {
// ....一些其他的逻辑
return "SVIP玩家客服";
}
if (recharge > 1_000_000) {
// ....一些其他的逻辑
return "专属客服";
}
return "找不到客服";
}

}

这里是一个对外接口,传进来一个充值金额,根据充值金额返回对应的客服.(一般来说不建议在controller层直接写逻辑,但是这里只是为了演示,不做过多无意义的封装).

每一个初学者都很简单能够写出这个逻辑.但是这样写的问题就是,如果充值金额大于1_000_000的时候,需要新增一个档位,那么就需要修改流程代码,强行插入逻辑,这不符合开闭原则,并且如果每一个if分支立里面的代码逻辑很长,这会导致代码可读性很差.
这段代码的重点在两个方面,一个是每个分支里面的逻辑,这里是如何获取相应的客服,另一个是判断条件,也就是判断什么时候进入什么分支,对于这种情况就非常适合使用策略模式来进行优化.

Version2

策略模式最主要的两点,分别对应着上面提到的两个重点.每个分支结构都是一个策略,每种策略都有进入条件,根据条件判断进入哪个分支.
策略模式一般会定义一个策略接口,定义策略的抽象方法,然后定义策略实现类,实现策略接口,并实现抽象方法.

这里首先定义一个策略接口:

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
package org.lin.strategicdesignpattern.version2;

/**
* ClassName:CustomerService
* Package: org.lin.strategicdesignpattern.version2
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/26 22:39
*/
public interface CustomerServiceV2 {

/*
* 这里定义进入每个策略的条件,让每个策略自己写自己的条件
* @param recharge
* @return java.lang.Boolean
* @author Xiaobin
* @since 2025/05/26 22:49
*/
Boolean support(Integer recharge);

/*
* 这里定义每个策略的具体逻辑
* @param recharge
* @return java.lang.String
* @author Xiaobin
* @since 2025/05/26 22:41
*/
String findCustomer(Integer recharge);
}

这个策略接口里面定义了两个事情,一个是判断进入这个策略的条件,一个是每个策略的具体逻辑.策略接口定义好了之后开始写策略实现类,对应上面一个if分支结构一个具体的策略实现类.

普通玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* 普通玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:40
*/
@Service
@Order(1) // 控制加载的顺序,优先级比默认高
public class NormalCustomerServiceV2Impl implements CustomerServiceV2 {

@Override
public Boolean support(Integer recharge) {
return recharge >0 && recharge <= 100;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "普通玩家客服";
}
}

VIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* SVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:44
*/
@Service
@Order(1) // 控制加载的顺序,优先级比默认高
public class SVIPCustomerServiceV2Impl implements CustomerServiceV2 {


@Override
public Boolean support(Integer recharge) {
return recharge > 100_000 && recharge <= 1_000_000;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "SVIP玩家客服";
}
}

SVIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* SVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:44
*/
@Service
@Order(1) // 控制加载的顺序,优先级比默认高
public class SVIPCustomerServiceV2Impl implements CustomerServiceV2 {


@Override
public Boolean support(Integer recharge) {
return recharge > 100_000 && recharge <= 1_000_000;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "SVIP玩家客服";
}
}

VVIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* VVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:43
*/
@Service
@Order(1) // 控制加载的顺序,优先级比默认高
public class VVIPCustomerServiceV2Impl implements CustomerServiceV2 {


@Override
public Boolean support(Integer recharge) {
return recharge > 10_000 && recharge <= 100_000;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "VVIP玩家客服";
}
}

专属玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* 专属玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:46
*/
@Service
@Order(1) // 控制加载的顺序,优先级比默认高
public class ExclusiveCustomerServiceV2Impl implements CustomerServiceV2 {

@Override
public Boolean support(Integer recharge) {
return recharge > 1_000_000;
}


@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "专属客服";
}
}

默认玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.lin.strategicdesignpattern.version2;

import org.springframework.stereotype.Service;

/**
* 默认的找不到客服实现类
*
* @author Xiaobin
* @since 2025/05/26 23:01
*/
@Service
public class DefaultCustomerServiceV2Impl implements CustomerServiceV2 {
@Override
public Boolean support(Integer recharge) {
return true;
}

@Override
public String findCustomer(Integer recharge) {
return "找不到客服";
}
}

上面定义了6个策略实现类,继承于策略接口,然后分别实现自己的逻辑.
然后我们就可以在程序里面引入所有的实现类,动态判断走哪个实现类,然后执行那个实现类的逻辑.

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
package org.lin.strategicdesignpattern.version2;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
* @author Xiaobin
* @since 2025/05/26 22:37
*/
@RestController
@RequestMapping("/customer/v2")
public class CustomerControllerV2 {

// 将所有实现了CustomerService接口的类都注入到customerServiceList中
@Resource
private List<CustomerServiceV2> customerServiceV2List;

@RequestMapping("/{recharge}")
public String customer(@PathVariable(value = "recharge") Integer recharge) {

// 遍历customerServiceList,找到第一个支持的类,然后调用findCustomer方法
for (CustomerServiceV2 customerServiceV2 : customerServiceV2List) {
if (customerServiceV2.support(recharge)) {
return customerServiceV2.findCustomer(recharge);
}
}
// 因为上面的customerServiceList中已经添加了默认的客服,默认客服的support方法返回true,所以正常执行的话就不会执行到这一步
// 所以如果执行到之里,说明程序异常了,这里抛出异常即可
throw new RuntimeException("没有找到对应的客服");
}
}

这里使用策略模式优化上面的代码,将不同的策略都注入到一个集合中,然后动态判断执行哪个策略
这个时候如果我们需要添加一个客服,只需要在CustomerService接口中添加一个实现类,然后写上该实现类自己的判断进入条件,和该实现类自己的逻辑,然后注入到customerServiceList中,就可以调用该实现类的逻辑,不再需要改主流程的代码

但是这里有一个问题点,就是策略的顺序问题
因为有默认客服的实现,但是默认客服实现类加载的顺序默认不可控,下面进行for循环的时候如果默认客服实现类加载的顺序不是最后面,那么就会执行默认的客服实现类的逻辑,而不是执行对应的客服
这里需要对customerServiceList里面的各个策略的顺序进行控制,因为除了默认的策略外,其余的策略进入的条件都是互斥的,所以其余策略的相互顺序并不重要,重要的只是默认的策略必须排在所有策略最后即可
那么如何控制注入的这个list里面每种策略的顺序呢?
使用@Order注解,控制不同策略加载的顺序,默认的策略加载的优先级最低,最后加载,排在最后
@Order(value) value越小的优先级越高.

其实到这一步已经是使用了策略模式了,并且看起来这个策略模式是不错的.但是还能不能再优化一点呢?
这里的优化点可以有下面的两个方向考虑:

  1. 当前各个策略的进入条件是各自持有的,分散在了各自的实现类中,这里考虑将条件集中管理;
  2. 当前这里是将所有的实现类以一个List的方式注入,然后每次执行的时候通过遍历这个List来找到对应的策略实现类.一般来讲这里的时间复杂度是O(n),还能有优化的空间.这里考虑通过工厂模式,在容器启动的时候加载所有的策略到工厂中,从工厂类中获取对应的策略实现类,然后执行对应的策略实现类.

Version3

首先考虑第一点,我们使用一个枚举,将所有的判断逻辑集中起来:

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
package org.lin.strategicdesignpattern.version4;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;

/**
* ClassName:UserTypeEnum
* Package: org.lin.strategicdesignpattern.version4
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/26 23:42
*/
@Getter
@AllArgsConstructor
public enum UserTypeEnum {

NORMAL(1, "普通用户", recharge -> recharge >0 && recharge <= 100),
VIP(2, "VIP用户", recharge -> recharge > 100 && recharge <= 10000),
VVIP(3, "VVIP用户", recharge -> recharge > 10_000 && recharge <= 100_000),
SVIP(4, "SVIP用户", recharge -> recharge > 100_000 && recharge <= 1_000_000),
EXCLUSIVE(5, "专属用户", recharge -> recharge > 1_000_000),
DEFAULT(6, "默认用户", recharge -> Boolean.FALSE),
;

private final Integer code;
private final String desc;
private final Function<Integer, Boolean> support;

public static UserTypeEnum getUserTypeEnum(Integer recharge) {
return Arrays.stream(UserTypeEnum.values())
.filter(userType -> userType.support.apply(recharge))
.findFirst().orElse(DEFAULT);
}

}

这个枚举里面每一个枚举量都有自己条件逻辑,然后在下面的策略实现类里面持有自己的枚举值,通过策略实现类 –> 枚举值 –> 枚举值对应的判断条件 这样的形式关联起来.

然后一样的,我们定义策略接口:

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
package org.lin.strategicdesignpattern.version4;

/**
* ClassName:CustomerService
* Package: org.lin.strategicdesignpattern.version2
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/26 22:39
*/
public interface CustomerServiceV4 {

/*
* 获取用户类型
* @return org.lin.strategicdesignpattern.version4.UserTypeEnum
* @author Xiaobin
* @since 2025/05/26 22:41
*/
UserTypeEnum getUserType();

/*
* 这里定义每个策略的具体逻辑
* @param recharge
* @return java.lang.String
* @author Xiaobin
* @since 2025/05/26 22:41
*/
String findCustomer(Integer recharge);
}

这个接口将Version2里面的support方法进行了修改,原本的support方法里面放的是各个策略实现类进入的判断条件,这里将由于将判断条件集中到了枚举类里面,所以需要让策略实现类持有枚举值,然后通过枚举值来让判断条件和策略实现类关联起来.

然后我们实现策略的实现类

普通玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
* 普通玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:40
*/
@Service
public class NormalCustomerServiceV4Impl implements CustomerServiceV4 {

@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.NORMAL;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "普通玩家客服";
}
}

VIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
* VIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:42
*/
@Service
public class VIPCustomerServiceV4Impl implements CustomerServiceV4 {


@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.VIP;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "VIP玩家客服";
}
}

SVIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
* SVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:44
*/
@Service
public class SVIPCustomerServiceV4Impl implements CustomerServiceV4 {


@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.SVIP;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "SVIP玩家客服";
}
}

VVIP玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
* VVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:43
*/
@Service
public class VVIPCustomerServiceV4Impl implements CustomerServiceV4 {


@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.VVIP;
}

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "VVIP玩家客服";
}
}

专属玩家客服策略实现类:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

/**
* 专属玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:46
*/
@Service
public class ExclusiveCustomerServiceV4Impl implements CustomerServiceV4 {

@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.EXCLUSIVE;
}


@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "专属客服";
}
}

默认玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.lin.strategicdesignpattern.version4;

import org.springframework.stereotype.Service;

/**
* 默认的找不到客服实现类
*
* @author Xiaobin
* @since 2025/05/26 23:01
*/
@Service
public class DefaultCustomerServiceV4Impl implements CustomerServiceV4 {


@Override
public UserTypeEnum getUserType() {
return null;
}

@Override
public String findCustomer(Integer recharge) {
return "找不到客服";
}
}

然后我们考虑上面的第二点,引入工厂模式,创建一个策略工厂.

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* 策略模式实现类工厂类
*
* @author Xiaobin
* @since 2025/05/27 00:02
*/
@Component
public class CustomerServiceFactory implements ApplicationContextAware {

private Map<UserTypeEnum, CustomerServiceV4> SERVICE_MAP;

@Resource
private ApplicationContext applicationContext; // 静态保存上下文

public CustomerServiceV4 getService(UserTypeEnum userTypeEnum) {
return SERVICE_MAP.getOrDefault(userTypeEnum, getDefaultCustomerService());
}


@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 从容器中获取全部的支付处理类,注册到map中
Map<String, CustomerServiceV4> customerServiceV4s = applicationContext.getBeansOfType(CustomerServiceV4.class);
SERVICE_MAP = customerServiceV4s.values().stream().filter(customerServiceV4 -> customerServiceV4.getUserType() != null)
.collect(Collectors.toConcurrentMap(CustomerServiceV4::getUserType, Function.identity()));
}

// 从容器中获取默认实现
private CustomerServiceV4 getDefaultCustomerService() {
return applicationContext.getBean("defaultCustomerServiceV4Impl", CustomerServiceV4.class);
}
}

这里创建一个工厂类,在容器初始化的时候,获取到所有的策略实现类,并且注册到map中,通过用户类型获取对应的策略实现类.

然后我们主流程就可以简化成下面这样:

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
package org.lin.strategicdesignpattern.version4;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
* @author Xiaobin
* @since 2025/05/26 22:37
*/
@RestController
@RequestMapping("/customer/v4")
public class CustomerControllerV4 {


@Resource
private CustomerServiceFactory customerServiceFactory;

@RequestMapping("/{recharge}")
public String customer(@PathVariable(value = "recharge") Integer recharge) {
return customerServiceFactory.getService(UserTypeEnum.getUserTypeEnum(recharge)).findCustomer(recharge);
}
}

可以看到的逻辑很清晰,首先根据充值金额判断拿到对应的用户类型,然后根据客户类型从工厂类中获取提前注册好的策略实现类,然后调用对应的策略实现类中的方法
这里优化的点有两点:

  1. 将条件判断逻辑集中到一个地方,即UserTypeEnum,然后每个策略分配一个UserType
  2. 通过工厂模式,在容器启动的时候加载所有的策略到工厂中,然后根据条件判断进入哪个策略

到这里我们想能不能再简化一点呢?
策略模式应该只保留本身各自逻辑,但是这里却引入和另外的一个概念就是用户类型,策略实现类需要持有用户类型,但是持有的实现方法有很多种,之类考虑使用注解的方式持有.把用户类型抽出来写成一个注解的形式,然后通过扫描注解的方式,将注解的策略注册到工厂中,然后根据条件判断进入哪个策略.

Version4

首先定义一个SupportUserType注解,这个注解可以加在类上面,在运行时生效,注解里面有一个UserType枚举的属性,表示当前类属于哪个UserType.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.lin.strategicdesignpattern.version5;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* ClassName:SupportUserType
* Package: org.lin.strategicdesignpattern.version5
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/27 01:31
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportUserType {

UserTypeEnumV5 value();
}

同样的,我们需要定义一个策略接口,如下,但是在这个接口里面不需要再显式地对策略进入条件进行抽象了,同时也不需要再定义策略实现类的用户方法,我们将这部份的内容,放到注解里面去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.lin.strategicdesignpattern.version5;

/**
* ClassName:CustomerService
* Package: org.lin.strategicdesignpattern.version2
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/26 22:39
*/
public interface CustomerServiceV5 {

/*
* 这里定义每个策略的具体逻辑
* @param recharge
* @return java.lang.String
* @author Xiaobin
* @since 2025/05/26 22:41
*/
String findCustomer(Integer recharge);
}

在上面的注解中持有一个UserType类型的枚举,这里同样需要将这个枚举定义出来:

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
package org.lin.strategicdesignpattern.version5;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;
import java.util.function.Function;

/**
* ClassName:UserTypeEnum
* Package: org.lin.strategicdesignpattern.version4
* Description:
*
* @author Xiaobin
* @version 1.0
* @create 2025/5/26 23:42
*/
@Getter
@AllArgsConstructor
public enum UserTypeEnumV5 {

NORMAL(1, "普通用户", recharge -> recharge >0 && recharge <= 100),
VIP(2, "VIP用户", recharge -> recharge > 100 && recharge <= 10000),
VVIP(3, "VVIP用户", recharge -> recharge > 10_000 && recharge <= 100_000),
SVIP(4, "SVIP用户", recharge -> recharge > 100_000 && recharge <= 1_000_000),
EXCLUSIVE(5, "专属用户", recharge -> recharge > 1_000_000),
DEFAULT(6, "默认用户", recharge -> Boolean.FALSE),
;

private final Integer code;
private final String desc;
private final Function<Integer, Boolean> support;

public static UserTypeEnumV5 getUserTypeEnum(Integer recharge) {
return Arrays.stream(UserTypeEnumV5.values())
.filter(userType -> userType.support.apply(recharge))
.findFirst().orElse(DEFAULT);
}


}

接下来依旧是需要定义几个策略实现类,这里通过在实现类上标注我们自定义的注解的方式 ,将策略实现类和用户类型进行绑定,进而和进入条件绑定.

普通玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.lin.strategicdesignpattern.version5;

import org.springframework.stereotype.Service;

/**
* 普通玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:40
*/
@Service
@SupportUserType(UserTypeEnumV5.NORMAL)
public class NormalCustomerServiceV5Impl implements CustomerServiceV5 {

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "普通玩家客服";
}
}

VIP玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.lin.strategicdesignpattern.version5;

import org.springframework.stereotype.Service;

/**
* VIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:42
*/
@Service
@SupportUserType(UserTypeEnumV5.VIP)
public class VIPCustomerServiceV5Impl implements CustomerServiceV5 {

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "VIP玩家客服";
}
}

VVIP玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.lin.strategicdesignpattern.version5;

import org.springframework.stereotype.Service;

/**
* VVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:43
*/
@Service
@SupportUserType(UserTypeEnumV5.VVIP)
public class VVIPCustomerServiceV5Impl implements CustomerServiceV5 {

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "VVIP玩家客服";
}
}

SVIP玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.lin.strategicdesignpattern.version5;

import org.springframework.stereotype.Service;

/**
* SVIP玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:44
*/
@Service
@SupportUserType(UserTypeEnumV5.SVIP)
public class SVIPCustomerServiceV5Impl implements CustomerServiceV5 {

@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "SVIP玩家客服";
}
}

专属玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.lin.strategicdesignpattern.version5;

import org.lin.strategicdesignpattern.version4.UserTypeEnum;
import org.springframework.stereotype.Service;

/**
* 专属玩家客服实现类
*
* @author Xiaobin
* @since 2025/05/26 22:46
*/
@Service
@SupportUserType(UserTypeEnumV5.EXCLUSIVE)
public class ExclusiveCustomerServiceV5Impl implements CustomerServiceV5 {


@Override
public String findCustomer(Integer recharge) {
// ....一些其他的逻辑
return "专属客服";
}
}

默认玩家客服策略实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.lin.strategicdesignpattern.version5;

import org.springframework.stereotype.Service;

/**
* 默认的找不到客服实现类
*
* @author Xiaobin
* @since 2025/05/26 23:01
*/
@Service
@SupportUserType(UserTypeEnumV5.DEFAULT)
public class DefaultCustomerServiceV5Impl implements CustomerServiceV5 {


@Override
public String findCustomer(Integer recharge) {
return "找不到客服";
}
}

同样需要一个策略工厂,在容器初始化的时候将所有的策略实现类加载到工厂中

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
48
49
package org.lin.strategicdesignpattern.version5;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* 策略模式实现类工厂类
*
* @author Xiaobin
* @since 2025/05/27 00:02
*/
@Component
public class CustomerServiceFactoryV5 implements ApplicationContextAware {

private final static Map<UserTypeEnumV5, CustomerServiceV5> SERVICE_MAP = new EnumMap<>(UserTypeEnumV5.class);

@Resource
private ApplicationContext applicationContext; // 静态保存上下文

public CustomerServiceV5 getService(UserTypeEnumV5 userTypeEnumV5) {
return SERVICE_MAP.getOrDefault(userTypeEnumV5, getDefaultCustomerService());
}


@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 从容器中获取全部的支付处理类,注册到map中
Map<String, CustomerServiceV5> customerServiceV5s = applicationContext.getBeansOfType(CustomerServiceV5.class);
customerServiceV5s.values().stream().filter(customerServiceV5 -> customerServiceV5.getClass().isAnnotationPresent(SupportUserType.class))
.forEach(customerServiceV5 -> SERVICE_MAP.put(AopUtils.getTargetClass(customerServiceV5).getAnnotation(SupportUserType.class).value(), customerServiceV5));
if (SERVICE_MAP.size() != UserTypeEnumV5.values().length) {
throw new RuntimeException("请检查是否注册了所有的策略类");
}
}

// 从容器中获取默认实现
private CustomerServiceV5 getDefaultCustomerService() {
return applicationContext.getBean("defaultCustomerServiceV5Impl", CustomerServiceV5.class);
}
}

由于将每个策略实现类的用户类型放在了注解里面,所以工厂里面加载所有实现类的方法也要对应这个改变,这里通过AopUtils.getTargetClass(customerServiceV5).getAnnotation(SupportUserType.class)来获取每个策略实现类上SupportUserType注解,而不是通过customerService.getClass().getAnnotation(SupportUserType.class),是因为考虑到策略实现类有可能被代理,一旦策略实现类被代理的话,后者可能会获取不到.
同时这里将存放策略实现类的容器由ConcurrentHashMap换成了EnumMap,进一步提升性能.
EnumMap 是 Java 集合框架中的一种特殊实现类,它是一个基于枚举类型的键(key)的 Map 实现。其内部使用数组存储值,使得其性能优于 HashMap 和 ConcurrentHashMap,尤其是在键是枚举类型时。
特点:

  1. 键必须为枚举类型:EnumMap 的键只能是枚举类型(Enum),不能是其他类型。
  2. 高性能:由于底层采用数组实现,访问速度比哈希表更快。
  3. 有序性:EnumMap 保持键的自然顺序(即枚举常量声明的顺序)。
  4. 线程不安全:与 HashMap 一样,EnumMap 不是线程安全的。如果需要并发访问,可以使用 Collections.synchronizedMap() 进行包装。

虽然第4点提到EnumMap是线程不安全的,但是由于这个场景里,工厂的初始化发生在容器启动阶段,并且在初始化完成之后,后续是只读的,所以这里并没有线程安全问题.