基于策略模式的通用接口设计

先理解一行代码

1
2
@Resource
Map<String, ProductService> ServiceMap = new ConcurrentHashMap<>(2);

此代码在 Spring Boot 启动时会被执行并创建一个 ConcurrentHashMap 实例, 以及它所引用的 ProductService 实现类的实例对象. 这些对象将被记录到map中

其中 String 就是 ProductService类的名字, 可以通过@Service(“serviceA”)来修改. 如果 ProductService 是一个接口. 那么 String 就是其对应的接口的名字.

实践一个简单的

  1. 编写 Controller 代码时, 经常遇到根据入参不同, 执行不同的 Service 逻辑的需求, 我们最常用的 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

@RestController
public class ProductController {
@Resource
private ServiceOrderA serviceOrderA;
@Resource
private ServiceOrderB serviceOrderB;

// ... 可能会有很多

@PostMapping("/order")
public String order(@RequestParam(value = "type") String type) {
if ("productA".equals(type)) {
return serviceOrderA.orderingProduct();
} else if ("productB".equals(type)) {
return serviceOrderB.orderingProduct();

// .... 可能会有很长

}else {
return "没有发现对应的产品处理策略";
}
}

}
  • 当 type 变多时, 需要注入很多的 private ServiceOrderA serviceOrderA; if 判断链也会很长. 代码杂乱没有章法, 且显得没有技术含量. 🙃
  1. 此时引入策略模式, 所有 Service 抽象成 ProductService
1
2
3
public interface ProductService {
String orderingProduct();
}
  1. 不同 Service 实现不同的 orderingProduct 逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    @Service("productA")
public class ProductAServiceImpl implements ProductService {
@Override
public String orderingProduct() {
return "成功订购产品A";
}
}

@Service("productB")
public class ProductBServiceImpl implements ProductService {
@Override
public String orderingProduct() {
return "成功订购产品B";
}
}
  1. 增加容器存放 Service
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class InterfaceContainer {

@Resource
Map<String, ProductService> configComponentMap = new ConcurrentHashMap<>(2);

public ProductService getProductStrategy(String productName) {

return configComponentMap.get(productName);
}

}

  1. 简化 Controller
1
2
3
4
5
6
7
@Resource
private InterfaceContainer interfaceContainer;
@PostMapping("/order1")
public String order1(@RequestParam(value = "type") String type) {
ProductService productService = interfaceContainer.getProductStrategy(type);
return productService != null ? productService.orderingProduct() : "没有发现对应的产品处理策略";
}

再来一个实际运用

设计一个前后端分离系统, 会涉及到很多单表的通用操作, 比如都根据id查询明细, 都通过自定义参数查询, 查询表的总行数用于分页, 单表写入, 单表更新等接口.

每个单表服务都写一个 controller ? 可以但不够优雅.

我们依旧采用策略模式优化.

此类通用操作先抽象成接口 ICommonQuery. 增加一个管理类来处理接口调用逻辑 CommonQueryManager

每个服务分别实现接口, 完成自己的逻辑.

外部暴露一个 controller. 通过 @PathVariable 获取 url 的 apiname, 然后去 map 中获取 Service. 进而调用到对应的服务.

ICommonQuery

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public interface ICommonQuery {
/**
* 根据id查询明细。
*
* @param id 资源id
* @return 资源
*/
Object selectOne(Long id) throws Exception;

/**
* 自定义查询
*
* @param parameterMap 查询参数
* @return 查询结果
*/
List<?> select(Map<String, String> parameterMap) throws Exception;

/**
* 查询数量
*
* @param parameterMap 查询参数
* @return 查询结果
*/
Long counts(Map<String, String> parameterMap) throws Exception;

/**
* 新增数据
*
* @param obj
* @return
*/
int insert(JSONObject obj, HttpServletRequest request) throws Exception;

/**
* 更新数据
*
* @param obj
* @return
*/
int update(JSONObject obj, HttpServletRequest request) throws Exception;

/**
* 删除数据
*
* @param id
* @return
*/
int delete(Long id, HttpServletRequest request) throws Exception;

/**
* 批量删除数据
*
* @param ids
* @return
*/
int deleteBatch(String ids, HttpServletRequest request) throws Exception;

/**
* 查询名称是否存在
*
* @param id
* @return
*/
int checkIsNameExist(Long id, String name) throws Exception;
}

CommonQueryManager

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

@Service
public class CommonQueryManager {

@Resource
private InterfaceContainer container;

/**
* 查询单条
*
* @param apiName 接口名称
* @param id ID
*/
public Object selectOne(String apiName, Long id) throws Exception {
if (StringUtils.isNotEmpty(apiName) && id!=null) {
return container.getCommonQuery(apiName).selectOne(id);
}
return null;
}

/**
* 查询
* @param apiName
* @param parameterMap
* @return
*/
public List<?> select(String apiName, Map<String, String> parameterMap)throws Exception {
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).select(parameterMap);
}
return new ArrayList<Object>();
}

/**
* 计数
* @param apiName
* @param parameterMap
* @return
*/
public Long counts(String apiName, Map<String, String> parameterMap)throws Exception {
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).counts(parameterMap);
}
return 0L;
}

/**
* 插入
* @param apiName
* @param obj
* @return
*/
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public int insert(String apiName, JSONObject obj, HttpServletRequest request) throws Exception{
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).insert(obj, request);
}
return 0;
}

/**
* 更新
* @param apiName
* @param obj
* @return
*/
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public int update(String apiName, JSONObject obj, HttpServletRequest request)throws Exception {
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).update(obj, request);
}
return 0;
}

/**
* 删除
* @param apiName
* @param id
* @return
*/
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public int delete(String apiName, Long id, HttpServletRequest request)throws Exception {
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).delete(id, request);
}
return 0;
}

/**
* 批量删除
* @param apiName
* @param ids
* @return
*/
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public int deleteBatch(String apiName, String ids, HttpServletRequest request)throws Exception {
if (StringUtils.isNotEmpty(apiName)) {
return container.getCommonQuery(apiName).deleteBatch(ids, request);
}
return 0;
}

/**
* 判断是否存在
* @param apiName
* @param id
* @param name
* @return
*/
public int checkIsNameExist(String apiName, Long id, String name) throws Exception{
if (StringUtils.isNotEmpty(apiName) && name!=null) {
return container.getCommonQuery(apiName).checkIsNameExist(id, name);
}
return 0;
}

}

InterfaceContainer

1
2
3
4
5
6
7
8
9
10
11

@Service
public class InterfaceContainer {
@Resource
private final Map<String, ICommonQuery> configComponentMap = new ConcurrentHashMap<>();

public ICommonQuery getCommonQuery(String apiName) {
return configComponentMap.get(apiName);
}
}

一个简单实现类

1
2
3
4
5
6
7
8
@Service("ypxx")
public class YPSerice implements ICommonQuery {
@Override
public Object selectOne(Long id) throws Exception {
return "YPSerice - selectOne 调用成功了";
}

// ......

ResourceController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@Api(tags = {"资源接口-通用查询"})
public class ResourceController {
@Resource
CommonQueryManager commonQueryManager;

@GetMapping(value = "/{apiName}/info")
@ApiOperation(value = "根据id获取信息")
public Response info(@PathVariable("apiName") String apiName,
@RequestParam("id") Long id,
HttpServletRequest request) throws Exception {
Map<String, Object> objectMap = new HashMap<String, Object>();
Object obj = commonQueryManager.selectOne(apiName, id);
objectMap.put("info", obj);
return Result.ok(objectMap);
}
}

效果如下