虽然设计模式常常被各种技术网站和教材提及,但是许多举例并不贴近我们的实际情况。有些示例甚至过于复杂,只为了解决一些与我们实际场景无关的问题,这让人感到脱离实际且将简单场景复杂化。本文希望能够通过一些信也国际化电商中真实的代码案例,来简单的谈谈设计模式如何影响了这些代码,同时也探讨这些代码为什么会恰好使用了这些设计模式。
突然有一天,大家被召集起来,有一个电商合作的大型需求需要紧急开发,大家议论纷纷,产品缓缓拿出需求文档:
我们需要和一家在线电商合作,用户可以在电商的收银台界面选择我们的分期产品,我们有一套订单体系,需要和电商平台的支付单一一对应,并且这个订单有多种状态,每一种状态我们都需要通知到第三方,最终支付完成,用户完成订单购买。
我们的研发同学二话不说,心想这还不简单,立马写起了代码:
class Transaction{
private Long id;
private String trxNo;
private String merchantOrderNo;
private BigDecimal amount;
}
class TransactionService {
private OuterCallService outerCallService;
private LoanService loanService;
public init()
//初始化订单
Transaction trx = new Transaction();
//初始化各种数值
trx.set();
trx.set();
outerCallService.notify(params);
}
public audit(Long trxId){
updateStatus(trxId, StatusEnum.AUDIT);
//发起借贷的审核请求
loanService.audit(req);
outerCallService.notify(params);
}
public paySuccess(){
updateStatus(trxId, StatusEnum.PAY_SUCCESS);
outerCallService.notify(params);
}
/**其他状态修改**/
}
class OuterCallService(){
public notify(Long trxId, String status){
//直接调用第三方接口
}
}
很快开发同学就开发出了一段代码,并且成功经过了测试同学的测试,快速完成了产品的紧急需求,并且上线了。但是当研发同学回头看看这段代码的时候心里充满了不安,不断地问自己,如果每一个状态的变化背后都需要不同的业务处理,而不再仅仅是notify了怎么办?如果又多接了一家电商怎么办?如果一些流程里面还需要加入一些特殊的逻辑,主流程代码就会被改动,测试回归范围怎么办?我的单元测试怎么办?需要被大规模改动吗?这些代码我好像一个原则都没遵守,全混在一起了呀。带着这些忐忑与不安,很快迎来了第二家电商。又是一天清晨,产品又开了一个需求评审会:
我们这一次的电商是一个线下场景,我们面对的是线下用户,订单流程和之前的差不多也是通知为主,但是我们有几个场景不需要通知。并且线下场景订单信息会有不同,需要额外增加店铺信息。审核成功后不再等用户确认,而是自动发起打款。有可能存在同一个商品被多个人同时消费的场景,我们需要对商品单独加锁,保证商品订单一对一。
开发同学心想完了,两家主流程不同,背后的第三方调用通知实现也不同,日后肯定会有更多的变化,于是开始更加详细的代码设计。为了能够分开处理多家电商的不同的流程场景,研发同学脑海中立马浮现出了一副策略模式的画面。
代码设计中的策略模式是一种行为型设计模式,它允许在运行时选择算法的行为,将每个算法封装在独立的类中,并使它们可以互相替换。以下是策略模式的特点:
总结:策略模式的特点包括封装算法、动态选择算法以及解耦算法和客户端。这些特点使得策略模式成为一种强大的工具,能够帮助我们设计灵活、可扩展的代码结构。
interface TransactionStrategy{
init(Object... params);
audit(Object... params);
paySuccess(Object... params);
}
//线下电商
class OnlineTransactionStrategy implements Strategy{
public init(Object... params){
lockProduct(params);
checkProductNotBeSaled(params);
}
public audit(Object... params){
loanService.audit(req);
}
public paySuccess(Object... params){
unlockProduct(params);
}
}
//线上电商
class OfflineTransactionStrategy implements Straegy{
public init(Object... params){
//空方法
}
public audit(Object... params){
loanService.audit(req);
}
public paySuccess(Object... params){
//空方法
}
}
//并且需要创建策略模式的环境类,以供外部调用
class TransactionStrategyContext {
public static Strategy getStrategy(String merchantNo) {
switch (merchantNo) {
case "onlien":
return new OnlineStrategy();
case "offline":
return new OfflineStrategy();
default:
throw new IllegalArgumentException("rewardType error!");
}
}
}
在完成了策略模式的编写后(Notify也以相同形式实现,具体略),当然上述的代码其实可以更加优雅,比如说能否实现自动注册(@PostConstruct),而不再是手动注册的形式等等,需要大家自行探索。研发同学又紧锣密鼓地开始了原始代码的改造。
class Transaction{
//略。。
}
class TransactionService {
private OuterCallService outerCallService;
private LoanService loanService;
private TransctionstrategyContext transctionstrategyContext;
public init(Object... params)
//初始化订单
Transaction trx = new Transaction();
//初始化各种数值
trx.set();
trx.set();
transactionStrategyContext.getStrategy(merchantNo).init(params);
if(needNotify){
notifyStrategyContext.getStrategy(merchantNo).notify(params);
}
}
public audit(Long trxId){
updateStatus(trxId, StatusEnum.AUDIT);
transactionStrategyContext.getStrategy(merchantNo).audit(params);
if(needNotify){
notifyStrategyContext.getStrategy(merchantNo).notify(params);
}
}
public paySuccess(){
updateStatus(trxId, StatusEnum.PAY_SUCCESS);
transactionStrategyContext.getStrategy(merchantNo).paySuccess(params);
if(needNotify){
notifyStrategyContext.getStrategy(merchantNo).notify(params);
}
}
/**其他状态修改**/
}
class OuterCallService(){
public notify(Long trxId, String status){
//直接调用第三方接口
}
}
修改完了这段代码,研发同学对着这个代码左看右看,心想自动发起打款该怎么做呀?
状态模式是一种行为型设计模式,用于处理对象在不同状态下的行为变化。它的主要特点如下:
单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供全局访问点。以下是单例模式的三个主要特点:
class Transaction{
//略。。
}
//状态类的环境类
//单例
class StatusChangeSingleContext{
//创建 StatusChangeSingleContext 的一个对象
private static StatusChangeSingleContext instance = new StatusChangeSingleContext();
//让构造函数为 private,这样该类就不会被实例化
private StatusChangeSingleContext(){}
//获取唯一可用的对象
public static StatusChangeSingleContext getInstance(){
return instance;
}
Map map = new HashMap<>();
public void put(String, StatusChange){
map.put(String, StatusChange);
}
public void invoke(String status, params){
map.get(status).changeStatus(params);
}
}
//统一管理的状态抽象方法,外部只需要调用changeStatus()
abstract class StatusChange{
private StatusChangeLogService statusChangeLogService;
doAction();
register();
void changeStatus(String status, Tranaction trx, Object... params){
doAction();
changeStatus(params);
statusChangeLogService.insertStatusChangeLog(params);
notifyStrategyContext.notify(params);
}
}
class InitStatusChange extends StatusChange{
private TransactionStrategyContext transctionstrategyContext;
private InitStatusChange(){}
public void register(){
StatusChangeSingleContext.getInstance().put("INIT", new InitStatusChange())
}
public void doAction{
transactionStrategyContext.getStrategy(merchantNo).init
}
}
class AuditStatusChange extends StatusChange{
private TransactionStrategyContext transctionstrategyContext;
private AuditStatusChange(){}
public void register(){
StatusChangeSingleContext.getInstance().put("AUDIT", new InitStatusChange())
}
public void doAction{
transactionStrategyContext.getStrategy(merchantNo).init
}
}
class PaySuccessStatusChange extends StatusChange{
private TransactionStrategyContext transctionstrategyContext;
private PaySuccessStatusChange(){}
public void register(){
StatusChangeSingleContext.getInstance().put("PAY_SUCCESS", new InitStatusChange())
}
public void doAction{
transactionStrategyContext.getStrategy(merchantNo).init
}
}
class TransactionService {
private StatusChange statusChange;
public init(Object... params)
//初始化订单
Transaction trx = new Transaction();
//初始化各种数值
trx.set();
trx.set();
StatusChangeSingleContext.getInstance().invoke("INIT",params);
}
public audit(Long trxId){
StatusChangeSingleContext.getInstance().invoke("AUDIT", params);
}
public paySuccess(){
StatusChangeSingleContext.getInstance().invoke("PAY_SUCCESS",params);
}
/**其他状态修改**/
}
至此,研发露出了满意的笑,对于多个电商,不同流程,都有了良好的适配。
尽管在实际的应用当中,仅仅订单管理所使用到的远不止这些设计模式,但是万变不离其宗,终究是不断地抽象,继承,多态。本文只是列举了三种常见的设计模式,所以在这里只是抛砖引玉。最后,无论是SOLID,还是各种设计原则,都并不是为了框定我们的设计而生的,他们只不过是一些前人走过的地方,恰巧走的人多了,就变成了路。设计模式也没有定式,如今衍生出来的设计模式也远不止23种,所有的这些理论、套路最后都只是服务于“好读,好改,好测”这三个终极目标。希望大家能够灵活应用,但是也不要走进靠设计模式“炫技”的怪圈。
xuzj ,现任信也科技国际化部门后端研发专家
Java、大数据、前端、测试等各种技术岗位热招中,欢迎扫码了解~