策略(Strategy)和工厂(Factory)是两种常见的设计模式,它们在不同的设计情景下可带来不同的好处
这两者也可以结合使用,以便在软件设计中提供更灵活且可维护的解决方案
工厂模式(Factory Pattern)
工厂模式是用于创建对象的模式,而不需要暴露对象创建的逻辑给客户端。在工厂方法模式中,一个方法返回一个对象,这个对象通常基于接口或抽象类。使用工厂模式意味着当你添加一个新的类(在这个场景中,新的支付服务)时,你不需要修改调用代码,只需修改工厂类即可。
PayChannelServiceFactory 在这个场景中就扮演了工厂的角色,根据给定的支付渠道(PayChannel),选择并返回正确的支付服务实现。
@Service
public class PayChannelServiceFactory {
@Autowired
private final Map<String, PayChannelService> serviceMap = new ConcurrentHashMap<String, PayChannelService>();
public PayChannelService get(PayChannel payChannel) {
String beanName = BeanNameUtils.getBeanName(payChannel.name(), "PayChannelService");
//组装出beanName,并从map中获取对应的bean
PayChannelService payChannelService = serviceMap.get(beanName);
if (payChannelService != null) {
return payChannelService;
} else {
throw new UnsupportedOperationException(
"No PayChannelService Found With payChannel : " + payChannel);
}
}
}
这里面借助了@Autowired注解,他可以把所有的PayChannelService的实现全部都注入到这个serviceMap里面,把bean name当做map的key,value就是对应的bean的实例。
策略模式(Strategy Pattern)
策略模式定义了一系列的算法或策略,并将它们封装起来,使得它们可以相互替换,且算法的变化不会影响到使用算法的客户端。这个模式的关键是定义一个策略接口,然后创建实现该接口的具体策略类。
在 PayChannelServiceFactory 中,PayChannelService 就是一个策略接口,而不同的支付渠道(如支付宝、微信等)的具体实现类就是不同的策略。利用工厂,根据传入的参数(在这里是 PayChannel)来选择使用哪个策略。
public interface PayChannelService {
/**
* 支付
* @param payChannelRequest
* @return
*/
PayChannelResponse pay(PayChannelRequest payChannelRequest);
/**
* 支付结果回调
* @param request
* @param response
*/
boolean nofity(HttpServletRequest request, HttpServletResponse response);
}
@Service("mockPayService")
@Slf4j
public class MockPayChannelServiceImpl implements PayChannelService {
@Override
public PayChannelResponse pay(PayChannelRequest payChannelRequest) {
return null;
}
@Override
public boolean nofity(HttpServletRequest request, HttpServletResponse response) {
return true;
}
}
@Service("wechatPayChannelService")
@Slf4j
public class WxPayChannelServiceImpl implements PayChannelService {
@Autowired
WxPayBean wxPayBean;
@Autowired
private PayApplicationService payApplicationService;
String serialNo;
@Override
public PayChannelResponse pay(PayChannelRequest payChannelRequest) {
WxPayChannelResponse resp=new WxPayChannelResponse();
try {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(wxPayBean.getAppId())
.setMchid(wxPayBean.getMchId())
.setDescription(payChannelRequest.getDescription())
.setOut_trade_no(payChannelRequest.getOrderId())
.setTime_expire(timeExpire)
//附加数据,暂时先设置为与商品信息一致
.setAttach(payChannelRequest.getAttach())
.setNotify_url(wxPayBean.getDomain().concat("/wxPay/payNotify"))
.setAmount(new Amount().setTotal(Integer.valueOf(String.valueOf(payChannelRequest.getAmount()))));
log.info("request {}", JSONUtil.toJsonStr(unifiedOrderModel));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
WxDomainEnum.CHINA.toString(),
BasePayApiEnum.NATIVE_PAY.toString(),
wxPayBean.getMchId(),
getSerialNumber(),
null,
wxPayBean.getKeyPath(),
JSONUtil.toJsonStr(unifiedOrderModel),
AuthTypeEnum.RSA.getCode()
);
log.info("response {}", response);
// 根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, wxPayBean.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
String body=response.getBody();
Map bodyMap=JSON.parseObject(body,Map.class);
resp.setPayUrl(bodyMap.get("code_url").toString());
resp.setSuccess(true);
return resp;
} catch (Exception e) {
log.error("pay error ", e);
resp.setSuccess(false);
return resp;
}
}
@Override
public boolean nofity(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> map = new HashMap<>(12);
try {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("支付通知密文 {}", result);
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
wxPayBean.getApiKey3(), wxPayBean.getPlatformCertPath());
log.info("支付通知明文 {}", plainText);
if (StrUtil.isEmpty(plainText)) {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
WxNotifyEntity wxNotifyEntity = JSON.parseObject(plainText, WxNotifyEntity.class);
PaySuccessEvent paySuccessEvent = new PaySuccessEvent();
paySuccessEvent.setChannelStreamId(wxNotifyEntity.getTransaction_id());
paySuccessEvent.setPaidAmount(MoneyUtils.centToYuan(Long.valueOf(wxNotifyEntity.getAmount().total())));
paySuccessEvent.setPayOrderId(wxNotifyEntity.getOut_trade_no());
paySuccessEvent.setPaySucceedTime(DateUtil.parseUTC(wxNotifyEntity.getSuccess_time()));
boolean paySuccessResult = payApplicationService.paySuccess(paySuccessEvent);
if(paySuccessResult){
response.setStatus(200);
map.put("code", SUCCESS.name());
map.put("message", SUCCESS.name());
}else{
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "内部处理失败");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
log.error("nofity error", e);
return false;
}
return true;
}
private String getSerialNumber() {
if (StrUtil.isEmpty(serialNo)) {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(wxPayBean.getCertPath());
if (null != certificate) {
serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
// 提前两天检查证书是否有效
boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayBean.getMchId(), -2);
log.info("cert is valid {} effectiveTime {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
}
}
System.out.println("serialNo:" + serialNo);
return serialNo;
}
}
策略+工厂的组合使用
将策略模式和工厂模式结合起来使用时,策略模式定义了操作的策略或行为,而工厂模式负责根据情况实例化具体的策略对象。
- 定义策略接口:定义一个PayChannelService接口,规定了所有支持的支付服务共同的操作。
- 实现策略接口:为每种支付方式实现一个具体的策略类,比如AlipayChannelService、WechatPayChannelService等。
- 创建工厂类:实现一个工厂类PayChannelServiceFactory。该工厂类拥有一个方法,能根据不同的输入(在这个例子中是PayChannel),返回对应的策略实现对象。
这种方式的优点是,当新增支付渠道或者修改现有渠道的支付逻辑时,你只需要添加或修改具体的策略实现类,而不需要修改调用这些策略的代码。这样,代码的维护性和扩展性都得到了提高,同时也保证了低耦合和高内聚的设计原则。