Skip to content

Commit 3931c1e

Browse files
author
LIANGLINJIANG888
committed
feat:更新12306专栏内容
1 parent 9cbb084 commit 3931c1e

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,335 @@
11
# **策略模式在项目设计中的应用**
2+
3+
## 摘要:
4+
在软件开发中,策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户,从而实现了业务逻辑与具体实现的解耦,为软件的可扩展性、可维护性和复用性提供了坚实的基础。
5+
6+
本文旨在探讨如何在12306项目中运用策略模式来优化业务流程,并通过实例分析其优势和实施步骤。
7+
8+
## 引言:
9+
随着业务的不断发展变化,软件系统需要具备灵活应对各种复杂场景的能力。传统的条件分支语句或类型继承体系往往导致系统结构臃肿,难以适应快速变化的业务需求。策略模式提供了一种更为优雅的解决方案,允许在运行时动态选择最适合的算法或流程。
10+
11+
## 1. 策略模式简介:
12+
策略模式定义了一族算法,分别封装起来,让它们之间可以互相替换。这些算法通常具有相同的接口,使得客户端在使用算法时无需知道具体的实现细节。
13+
14+
策略模式主要包含三个角色:上下文(Context)、策略(Strategy)和客户端(Client)。
15+
16+
## 2. 策略模式的优势:
17+
- 提高系统的灵活性和可扩展性。
18+
- 避免使用多重条件转移语句。
19+
- 提供管理相关的算法族的方法。
20+
- 符合开放封闭原则,易于扩展新的操作。
21+
- 符合单一职责原则,每个类只负责一件事情。
22+
23+
## 3. 策略模式在项目中的应用:
24+
以下是12306项目中策略模式的实际使用案例,展示如何利用策略模式进行业务代码的优化。
25+
26+
### 案例分析:
27+
对12306项目中支付宝支付组件的策略模式使用场景进行案例分析。
28+
29+
- 第一步:定义策略接口
30+
```java
31+
/**
32+
* 策略执行抽象
33+
*/
34+
public interface AbstractExecuteStrategy<REQUEST, RESPONSE> {
35+
36+
/**
37+
* 执行策略标识
38+
*/
39+
default String mark() {
40+
return null;
41+
}
42+
43+
/**
44+
* 执行策略范匹配标识
45+
*/
46+
default String patternMatchMark() {
47+
return null;
48+
}
49+
50+
/**
51+
* 执行策略
52+
*
53+
* @param requestParam 执行策略入参
54+
*/
55+
default void execute(REQUEST requestParam) {
56+
57+
}
58+
59+
/**
60+
* 执行策略,带返回值
61+
*
62+
* @param requestParam 执行策略入参
63+
* @return 执行策略后返回值
64+
*/
65+
default RESPONSE executeResp(REQUEST requestParam) {
66+
return null;
67+
}
68+
}
69+
70+
```
71+
- 第二步:实现不同的具体策略类
72+
```java
73+
/**
74+
* 阿里支付回调组件
75+
*/
76+
@Slf4j
77+
@Service
78+
@RequiredArgsConstructor
79+
public final class AliPayCallbackHandler extends AbstractPayCallbackHandler implements AbstractExecuteStrategy<PayCallbackRequest, Void> {
80+
81+
private final PayService payService;
82+
83+
@Override
84+
public void callback(PayCallbackRequest payCallbackRequest) {
85+
AliPayCallbackRequest aliPayCallBackRequest = payCallbackRequest.getAliPayCallBackRequest();
86+
PayCallbackReqDTO payCallbackRequestParam = PayCallbackReqDTO.builder()
87+
.status(TradeStatusEnum.queryActualTradeStatusCode(aliPayCallBackRequest.getTradeStatus()))
88+
.payAmount(aliPayCallBackRequest.getBuyerPayAmount())
89+
.tradeNo(aliPayCallBackRequest.getTradeNo())
90+
.gmtPayment(aliPayCallBackRequest.getGmtPayment())
91+
.orderSn(aliPayCallBackRequest.getOrderRequestId())
92+
.build();
93+
payService.callbackPay(payCallbackRequestParam);
94+
}
95+
96+
@Override
97+
public String mark() {
98+
return PayChannelEnum.ALI_PAY.name();
99+
}
100+
101+
public void execute(PayCallbackRequest requestParam) {
102+
callback(requestParam);
103+
}
104+
}
105+
106+
/**
107+
* 阿里支付组件
108+
*/
109+
@Slf4j
110+
@Service
111+
@RequiredArgsConstructor
112+
public class AliPayNativeHandler extends AbstractPayHandler implements AbstractExecuteStrategy<PayRequest, PayResponse> {
113+
114+
private final AliPayProperties aliPayProperties;
115+
116+
@SneakyThrows(value = AlipayApiException.class)
117+
@Override
118+
@Retryable(value = ServiceException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1.5))
119+
public PayResponse pay(PayRequest payRequest) {
120+
AliPayRequest aliPayRequest = payRequest.getAliPayRequest();
121+
AlipayConfig alipayConfig = BeanUtil.convert(aliPayProperties, AlipayConfig.class);
122+
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
123+
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
124+
model.setOutTradeNo(aliPayRequest.getOrderSn());
125+
model.setTotalAmount(aliPayRequest.getTotalAmount().toString());
126+
model.setSubject(aliPayRequest.getSubject());
127+
model.setProductCode("FAST_INSTANT_TRADE_PAY");
128+
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
129+
request.setNotifyUrl(aliPayProperties.getNotifyUrl());
130+
request.setBizModel(model);
131+
try {
132+
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
133+
log.info("发起支付宝支付,订单号:{},子订单号:{},订单请求号:{},订单金额:{} \n调用支付返回:\n\n{}\n",
134+
aliPayRequest.getOrderSn(),
135+
aliPayRequest.getOutOrderSn(),
136+
aliPayRequest.getOrderRequestId(),
137+
aliPayRequest.getTotalAmount(),
138+
JSONObject.toJSONString(response));
139+
if (!response.isSuccess()) {
140+
throw new ServiceException("调用支付宝发起支付异常");
141+
}
142+
return new PayResponse(StrUtil.replace(StrUtil.replace(response.getBody(), "\"", "'"), "\n", ""));
143+
} catch (AlipayApiException ex) {
144+
throw new ServiceException("调用支付宝支付异常");
145+
}
146+
}
147+
148+
@Override
149+
public String mark() {
150+
return StrBuilder.create()
151+
.append(PayChannelEnum.ALI_PAY.name())
152+
.append("_")
153+
.append(PayTradeTypeEnum.NATIVE.name())
154+
.toString();
155+
}
156+
157+
@Override
158+
public PayResponse executeResp(PayRequest requestParam) {
159+
return pay(requestParam);
160+
}
161+
}
162+
163+
/**
164+
* 阿里支付组件
165+
*
166+
*/
167+
@Slf4j
168+
@Service
169+
@RequiredArgsConstructor
170+
public class AliRefundNativeHandler extends AbstractRefundHandler implements AbstractExecuteStrategy<RefundRequest, RefundResponse> {
171+
172+
private final AliPayProperties aliPayProperties;
173+
174+
private final static String SUCCESS_CODE = "10000";
175+
176+
private final static String FUND_CHANGE = "Y";
177+
178+
@Retryable(value = {ServiceException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1.5))
179+
@SneakyThrows(value = AlipayApiException.class)
180+
@Override
181+
public RefundResponse refund(RefundRequest payRequest) {
182+
AliRefundRequest aliRefundRequest = payRequest.getAliRefundRequest();
183+
AlipayConfig alipayConfig = BeanUtil.convert(aliPayProperties, AlipayConfig.class);
184+
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
185+
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
186+
model.setOutTradeNo(aliRefundRequest.getOrderSn());
187+
model.setTradeNo(aliRefundRequest.getTradeNo());
188+
BigDecimal payAmount = aliRefundRequest.getPayAmount();
189+
BigDecimal refundAmount = payAmount.divide(new BigDecimal(100));
190+
model.setRefundAmount(refundAmount.toString());
191+
model.setOutRequestNo(SnowflakeIdUtil.nextIdStr());
192+
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
193+
request.setBizModel(model);
194+
try {
195+
AlipayTradeRefundResponse response = alipayClient.execute(request);
196+
String responseJson = JSONObject.toJSONString(response);
197+
log.info("发起支付宝退款,订单号:{},交易凭证号:{},退款金额:{} \n调用退款响应:\n\n{}\n",
198+
aliRefundRequest.getOrderSn(),
199+
aliRefundRequest.getTradeNo(),
200+
aliRefundRequest.getPayAmount(),
201+
responseJson);
202+
if (!StrUtil.equals(SUCCESS_CODE, response.getCode()) || !StrUtil.equals(FUND_CHANGE, response.getFundChange())) {
203+
throw new ServiceException("退款失败");
204+
}
205+
return new RefundResponse(TradeStatusEnum.TRADE_CLOSED.tradeCode(), response.getTradeNo());
206+
} catch (AlipayApiException e) {
207+
throw new ServiceException("调用支付宝退款异常");
208+
}
209+
}
210+
211+
@Override
212+
public String mark() {
213+
return StrBuilder.create()
214+
.append(PayChannelEnum.ALI_PAY.name())
215+
.append("_")
216+
.append(PayTradeTypeEnum.NATIVE.name())
217+
.append("_")
218+
.append(TradeStatusEnum.TRADE_CLOSED.tradeCode())
219+
.toString();
220+
}
221+
222+
@Override
223+
public RefundResponse executeResp(RefundRequest requestParam) {
224+
return refund(requestParam);
225+
}
226+
}
227+
```
228+
- 第三步:创建上下文环境类
229+
```java
230+
/**
231+
* 策略选择器
232+
**/
233+
public class AbstractStrategyChoose implements ApplicationListener<ApplicationInitializingEvent> {
234+
235+
/**
236+
* 执行策略集合
237+
*/
238+
private final Map<String, AbstractExecuteStrategy> abstractExecuteStrategyMap = new HashMap<>();
239+
240+
/**
241+
* 根据 mark 查询具体策略
242+
*
243+
* @param mark 策略标识
244+
* @param predicateFlag 匹配范解析标识
245+
* @return 实际执行策略
246+
*/
247+
public AbstractExecuteStrategy choose(String mark, Boolean predicateFlag) {
248+
if (predicateFlag != null && predicateFlag) {
249+
return abstractExecuteStrategyMap.values().stream()
250+
.filter(each -> StringUtils.hasText(each.patternMatchMark()))
251+
.filter(each -> Pattern.compile(each.patternMatchMark()).matcher(mark).matches())
252+
.findFirst()
253+
.orElseThrow(() -> new ServiceException("策略未定义"));
254+
}
255+
return Optional.ofNullable(abstractExecuteStrategyMap.get(mark))
256+
.orElseThrow(() -> new ServiceException(String.format("[%s] 策略未定义", mark)));
257+
}
258+
259+
/**
260+
* 根据 mark 查询具体策略并执行
261+
*
262+
* @param mark 策略标识
263+
* @param requestParam 执行策略入参
264+
* @param <REQUEST> 执行策略入参范型
265+
*/
266+
public <REQUEST> void chooseAndExecute(String mark, REQUEST requestParam) {
267+
AbstractExecuteStrategy executeStrategy = choose(mark, null);
268+
executeStrategy.execute(requestParam);
269+
}
270+
271+
/**
272+
* 根据 mark 查询具体策略并执行
273+
*
274+
* @param mark 策略标识
275+
* @param requestParam 执行策略入参
276+
* @param predicateFlag 匹配范解析标识
277+
* @param <REQUEST> 执行策略入参范型
278+
*/
279+
public <REQUEST> void chooseAndExecute(String mark, REQUEST requestParam, Boolean predicateFlag) {
280+
AbstractExecuteStrategy executeStrategy = choose(mark, predicateFlag);
281+
executeStrategy.execute(requestParam);
282+
}
283+
284+
/**
285+
* 根据 mark 查询具体策略并执行,带返回结果
286+
*
287+
* @param mark 策略标识
288+
* @param requestParam 执行策略入参
289+
* @param <REQUEST> 执行策略入参范型
290+
* @param <RESPONSE> 执行策略出参范型
291+
* @return
292+
*/
293+
public <REQUEST, RESPONSE> RESPONSE chooseAndExecuteResp(String mark, REQUEST requestParam) {
294+
AbstractExecuteStrategy executeStrategy = choose(mark, null);
295+
return (RESPONSE) executeStrategy.executeResp(requestParam);
296+
}
297+
298+
@Override
299+
public void onApplicationEvent(ApplicationInitializingEvent event) {
300+
Map<String, AbstractExecuteStrategy> actual = ApplicationContextHolder.getBeansOfType(AbstractExecuteStrategy.class);
301+
actual.forEach((beanName, bean) -> {
302+
AbstractExecuteStrategy beanExist = abstractExecuteStrategyMap.get(bean.mark());
303+
if (beanExist != null) {
304+
throw new ServiceException(String.format("[%s] Duplicate execution policy", bean.mark()));
305+
}
306+
abstractExecuteStrategyMap.put(bean.mark(), bean);
307+
});
308+
}
309+
}
310+
```
311+
- 第四步:通过策略模式封装支付回调渠道,支付回调时动态选择对应的支付回调组件
312+
```java
313+
@PostMapping("/api/pay-service/callback/alipay")
314+
public void callbackAlipay(@RequestParam Map<String, Object> requestParam) {
315+
PayCallbackCommand payCallbackCommand = BeanUtil.mapToBean(requestParam, PayCallbackCommand.class, true, CopyOptions.create());
316+
payCallbackCommand.setChannel(PayChannelEnum.ALI_PAY.getCode());
317+
payCallbackCommand.setOrderRequestId(requestParam.get("out_trade_no").toString());
318+
payCallbackCommand.setGmtPayment(DateUtil.parse(requestParam.get("gmt_payment").toString()));
319+
PayCallbackRequest payCallbackRequest = PayCallbackRequestConvert.command2PayCallbackRequest(payCallbackCommand);
320+
/**
321+
* {@link AliPayCallbackHandler}
322+
*/
323+
// 策略模式:通过策略模式封装支付回调渠道,支付回调时动态选择对应的支付回调组件
324+
abstractStrategyChoose.chooseAndExecute(payCallbackRequest.buildMark(), payCallbackRequest);
325+
}
326+
```
327+
328+
## 4. 实施步骤:
329+
- 识别可变的部分并将其封装成独立的策略类。
330+
- 确保所有的策略类遵循同一个策略接口。
331+
- 在上下文环境中设置策略对象,并在运行时决定使用哪一种策略。
332+
- 客户端代码调用上下文环境的执行方法,由上下文环境负责使用当前的策略对象来执行请求的操作。
333+
334+
## 结论:
335+
策略模式通过将算法的定义从使用它们的客户端代码中分离出来,不仅提高了代码的复用性与可维护性,还极大地提升了系统的灵活性。在项目实践过程中,适时引入策略模式,可以帮助开发者有效地管理和优化复杂的业务流程,以适应不断变化的业务需求。通过对策略模式的合理应用,项目团队可以更高效地响应市场变化,提升客户满意度,最终推动业务的持续增长和成功。

0 commit comments

Comments
 (0)