当前位置:首页 > 新闻动态 > 网站文章

设计模式在电商分期系统中的应用

来源: 浏览:118 时间:2023-09-20

1 引言

虽然设计模式常常被各种技术网站和教材提及,但是许多举例并不贴近我们的实际情况。有些示例甚至过于复杂,只为了解决一些与我们实际场景无关的问题,这让人感到脱离实际且将简单场景复杂化。本文希望能够通过一些信也国际化电商中真实的代码案例,来简单的谈谈设计模式如何影响了这些代码,同时也探讨这些代码为什么会恰好使用了这些设计模式。

2 N家电商,一种订单

突然有一天,大家被召集起来,有一个电商合作的大型需求需要紧急开发,大家议论纷纷,产品缓缓拿出需求文档:

我们需要和一家在线电商合作,用户可以在电商的收银台界面选择我们的分期产品,我们有一套订单体系,需要和电商平台的支付单一一对应,并且这个订单有多种状态,每一种状态我们都需要通知到第三方,最终支付完成,用户完成订单购买。

我们的研发同学二话不说,心想这还不简单,立马写起了代码:

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了怎么办?如果又多接了一家电商怎么办?如果一些流程里面还需要加入一些特殊的逻辑,主流程代码就会被改动,测试回归范围怎么办?我的单元测试怎么办?需要被大规模改动吗?这些代码我好像一个原则都没遵守,全混在一起了呀。带着这些忐忑与不安,很快迎来了第二家电商。又是一天清晨,产品又开了一个需求评审会:

我们这一次的电商是一个线下场景,我们面对的是线下用户,订单流程和之前的差不多也是通知为主,但是我们有几个场景不需要通知。并且线下场景订单信息会有不同,需要额外增加店铺信息。审核成功后不再等用户确认,而是自动发起打款。有可能存在同一个商品被多个人同时消费的场景,我们需要对商品单独加锁,保证商品订单一对一。

开发同学心想完了,两家主流程不同,背后的第三方调用通知实现也不同,日后肯定会有更多的变化,于是开始更加详细的代码设计。为了能够分开处理多家电商的不同的流程场景,研发同学脑海中立马浮现出了一副策略模式的画面。

3 策略模式

代码设计中的策略模式是一种行为型设计模式,它允许在运行时选择算法的行为,将每个算法封装在独立的类中,并使它们可以互相替换。以下是策略模式的特点:

  1. 封装算法:策略模式通过将不同的算法封装在各自的类中,使得每个算法都可以独立于其他算法进行修改和维护,这样做可以保持代码的灵活性和可维护性。
  2. 动态选择算法:策略模式允许在运行时根据不同的需求选择合适的算法。通过定义一个公共的接口或基类,客户端可以使用不同的具体策略对象来执行特定的算法,这种灵活性使得系统能够根据需要动态的切换算法,而无需修改核心代码。
  3. 解耦算法和客户端:策略模式可以将算法的实现与调用算法的客户端代码解耦。客户端只需要关注选择合适的策略对象并将其传递给上下文环境,而无需了解具体算法的实现细节,这样可以降低算法的复杂性,并且使得代码更加清晰易懂。

总结:策略模式的特点包括封装算法、动态选择算法以及解耦算法和客户端。这些特点使得策略模式成为一种强大的工具,能够帮助我们设计灵活、可扩展的代码结构。

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){
        //直接调用第三方接口
    }
}

修改完了这段代码,研发同学对着这个代码左看右看,心想自动发起打款该怎么做呀?

4 状态模式

状态模式是一种行为型设计模式,用于处理对象在不同状态下的行为变化。它的主要特点如下:

  1. 封装了对象的状态:状态模式将对象的不同状态封装成独立的类,每个类负责处理该状态下的行为。通过封装,可以使得状态之间的转换更加清晰和可控。
  2. 通过多态实现状态切换:状态模式利用多态性来实现状态切换,即对象在不同状态下具有不同的行为。通过调用不同状态对应的方法,对象可以根据当前状态自动切换到合适的行为。
  3. 遵循开闭原则:状态模式能够很好地支持新的状态添加,而不需要修改已有的代码。当需要新增状态时,只需增加新的状态类,并在上下文中进行相应的关联,而无需修改原有的状态类或上下文类。

5 单例模式

单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供全局访问点。以下是单例模式的三个主要特点:

  1. 独一无二:单例模式确保一个类只有一个实例存在于内存中。这意味着无论何时使用该类,都只会获得同一个实例。这对于需要共享资源、限制对象创建数量或确保系统中某个状态的唯一性非常有用。
  2. 全局访问:由于单例模式只允许存在一个实例,因此可以方便地通过全局访问点来获取该实例。这样可以避免频繁地传递对象实例,简化代码结构,并使多个组件之间更容易通信。
  3. 延迟初始化:单例模式通常采用延迟初始化的方式创建实例,即在第一次请求获取实例时才进行实例化操作。这样能够避免不必要的资源消耗和性能损失,尤其在大型应用程序中更为重要。总之,单例模式通过独一无二、全局访问和延迟初始化等特点来提供一个全局唯一的实例,简化代码结构,提高系统性能,以及确保特定资源或状态的唯一性。
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);
     }
/**其他状态修改**/
}

至此,研发露出了满意的笑,对于多个电商,不同流程,都有了良好的适配。

6 写在最后

尽管在实际的应用当中,仅仅订单管理所使用到的远不止这些设计模式,但是万变不离其宗,终究是不断地抽象,继承,多态。本文只是列举了三种常见的设计模式,所以在这里只是抛砖引玉。最后,无论是SOLID,还是各种设计原则,都并不是为了框定我们的设计而生的,他们只不过是一些前人走过的地方,恰巧走的人多了,就变成了路。设计模式也没有定式,如今衍生出来的设计模式也远不止23种,所有的这些理论、套路最后都只是服务于“好读,好改,好测”这三个终极目标。希望大家能够灵活应用,但是也不要走进靠设计模式“炫技”的怪圈。

作者介绍

xuzj ,现任信也科技国际化部门后端研发专家

出处:https://mp.weixin.qq.com/s/RlYKT-eBiumFv95jqUi0cA

地址 · ADDRESS

地址:建邺区新城科技园嘉陵江东街18号2层

邮箱:309474043@qq.Com

点击查看更多案例

联系 · CALL TEL

400-8793-956

售后专线:025-65016872

业务QQ:309474043    售后QQ:1850555641

©南京安优网络科技有限公司 版权所有   苏ICP备12071769号-4  网站地图