当前位置:坤哥网-kungge-一个公司项目的代码重构案例

一个公司项目的代码重构案例

2019/6/19 0:01:55 kungge阅读(105) 评论(0)


原代码结构介绍


    本次重构的项目是一个接口项目,本项目中包含多个接口。

先来看一下原来代码的设计,一个类文件达到了三千多行代码,所有的接口的主要逻辑都在该类中。

有一个方法入口,使用 switch 根据调用者调用的接口名称来调用不同的方法,一个方法就是一个接口的逻辑。


重构介绍


    本次重构将不同的接口分割到不同的类中,一个接口使用一个服务类,这些服务类继承一个服务基类 BaseSvc,在这个类中定义一些公共的成员:日志对象、业务操作类对象。


    我们线上提现系列接口都是是以产品为主,一个产品一个项目,在项目中接不同的资方,根据资方用if else 做不同的判断处理,由于代码都堆在一起,这样的弊端是,当修改某个资方的代码的时候,很有可能会影响到其他资方,而且后期很不好扩展与维护。


    大部分资方的逻辑代码其实是大同小异的,所以本次重构是一个接口定义一个抽象的基类,主要逻辑代码都包含在该基类中,不同的资方再定义成继承该基类的子类,尽量将逻辑代码粒度划分成最小,定义成不同的虚方法,当具体的资方子类有不同的业务逻辑时,再去重写基类的方法,这样能最大化达到代码的重用。


    根据调用端传过来的参数去具体实例化不同的子类。

    定义了一个静态字典用来存储不同的子类。

    定义了一个接口实例化的服务工厂接口 ISvcFactory,在该接口中定义了实例化每个服务接口的方法。

在构造函数中进行初始化,数据从一个 json 格式的配制文件中取。先来看下服务工厂配置,根据LoanSideKey,去实例化不同的服务工厂,一个资方对应一个服务工厂类,比如这个是外贸的服务工厂类 FoticSvcFactory,现在和包贷只接了一个资方,以后加了新的资方只需要新建这个资方服务工厂类,然后按这种格式添加一下配置。


    这里专门建了一个工厂类的文件夹 SvcModuleFactories,我们来看下:

02aa5ecf-9546-4c7d-aa9e-aa3d1358cbf4.png

FoticSvcFactory 实现了服务工厂接口 ISvcFactory,方法实现具体去实例化该资方对应的类,如 

 public InsertVBSSvc CreateInsertVBSSvc()
  {
     return new FoticInsertVBSSvc();
   }

    FoticInsertVBSSvc 就是外贸的提现服务类,它是继承提现服务基类 InsertVBSSvc,这个类内部的重构后面再讲,我们先回到服务入口的那个类 HBDSProvider。

初始化服务工厂字典,从配置文件中去取,然后通过反射实例化不同资方的服务工厂类的实例,加入到字典中。这里定义一个服务获取类 SvcAccess,内部定义了一个方法,根据调用端传过来的资方服务工厂实例和接口名称,去实例化对应的资方服务子类。比如传过来的是外贸的服务工厂类 FoticSvcFactory,接口名称是提现,那么最终会实例化外贸的提现服务子类。


提现接口重构


    因为提现接口的逻辑最复杂,本次以体现接口来作讲解,提现接口逻辑很多,所以内部又进行了重构。

提供了一个入口方法 Start(),将不同的逻辑尽量细分,提取到不同的方法中,每个方法都定义成虚方法,供子类去重写。

    定义了一个 SetExcute 方法来设置方法的执行顺序,通过事件注册的方式来实现。这个方法也是一个虚方法,这样子类可以重写该方法来设置自己的执行顺序。下面讲下订单校验的方法 CheckOrderRule。

先看下豆豆钱的数据校验:

9e933c20-231c-42d7-b146-9991fb4eed80.png

本次重构将这些校验划分到一个模块中,叫 OrderRuleModule,在这个模块当中提供一个入口,统一处理所有的校验。

数据校验都有一个共同特点就是通过了继续往后走,不通过则立即返回,而且执行顺序是串行的,基于这个特点,定义了一个校验基础类 BaseHandler,不同的校验定义成不同的子类,都继承这个基类,为了方便统一管理都放到一个文件夹中 OrderRules:

ca205590-6511-425d-9065-e80ea9ab4b0f.png

先来看下 这个基类吧:

    /// <summary>
    /// 校验基础类
    /// @author wankun
    /// </summary>
    public abstract class BaseHandler
    {
        protected static ILogger _Logger = LogFactory.CreateLogger(typeof(BaseHandler));

        protected HBDSBLL _hBDSBLL = null;

        /// <summary>
        /// 下一个处理者
        /// </summary>
        protected BaseHandler _nextHandler;

        /// <summary>
        /// 校验通过
        /// </summary>
        protected static int Success = OrderRuleVar.HandleSuccess;
        /// <summary>
        /// 校验不通过
        /// </summary>
        protected static int Defeat = OrderRuleVar.HandleDefeat;
        /// <summary>
        /// 校验异常
        /// </summary>
        protected static int Exception = OrderRuleVar.HandleException;

        #region ctr
        public BaseHandler() {
            if (_hBDSBLL == null)
            {
                _hBDSBLL = new HBDSBLL();
            }
        }
        public BaseHandler(HBDSBLL hBDSBLL)
        {
            if (_hBDSBLL == null)
            {
                _hBDSBLL = hBDSBLL;
            }
        }
        #endregion
        
        /// <summary>
        /// 处理名称
        /// </summary>
        protected virtual string HandlerName
        {
            get
            {
                return "校验基础类";
            }
        }

        /// <summary>
        /// 设置下一个处理者
        /// </summary>
        /// <param name="handler"></param>
        public void SetNextHandler(BaseHandler  handler)
        {
            this._nextHandler = handler;
        }

        /// <summary>
        /// 处理入口
        /// </summary>
        /// <param name="baseModel"></param>
        /// <returns></returns>
        public BaseOrderRuleResult HandleRule(BaseOrderRuleModel baseModel)
        {
            _Logger.Info($"开始进行【{HandlerName}】【{baseModel.HBDSInfo.OrderId}】【{baseModel.HBDSInfo.IdentityNo}】");

            try
            {
                var checkResult = CheckResult(baseModel);

                checkResult.HandlerName = HandlerName;

                _Logger.Info($"【{HandlerName}】【{baseModel.HBDSInfo.OrderId}】【{baseModel.HBDSInfo.IdentityNo}】完成【{(checkResult.ResultCode==Success?"通过":"不通过")}】");

                return checkResult.ResultCode == Success ? (_nextHandler!=null?_nextHandler.HandleRule(baseModel):checkResult ): checkResult;
            }
            catch (Exception ex)
            {
                _Logger.Error($"进行【{HandlerName}】【{baseModel.HBDSInfo.OrderId}】【{baseModel.HBDSInfo.IdentityNo}】发生异常【{ex.Message}】【{ex}】");
                throw ex;
            }       
        }

        /// <summary>
        /// 校验
        /// </summary>
        /// <param name="baseModel"></param>
        /// <returns></returns>
        protected virtual BaseOrderRuleResult CheckResult(BaseOrderRuleModel baseModel)
        {
            return new BaseOrderRuleResult()
            {
                ResultCode = Success
            };
        }

        /// <summary>
        /// 返回失败结果
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected virtual BaseOrderRuleResult RtnDefeatResult(string msg)
        {
            return new BaseOrderRuleResult()
            {
                ResultCode = Defeat,
                ResultMsg = msg
            };
        }

    定义了下一个处理者,处理名称供子类重写。

    定义了一个入口方法,在入口方法中调用处理逻辑的方法 CheckResult,这个方法也是虚方法,供子类重写。校验通过则调用下一个校验规则,如果不通过则立即返回到调用端。这个调用顺序如何设定?

通过配置的方式,我们打开之前介绍的那个配置文件,看 OrderRules 这个字段:993f40fe-952c-471c-8837-964953bf4a1e.png

里面配置了不同的资方不同的校验规则使用不同的类,ExecIndex 是数字类型,按从小到大依次执行,如果是0表示不执行。

    具体的执行是通过 OrderRuleModule 这个类的入口方法 Excute 来触发。在这个类中用一个静态字典来存储这些校验类的实例,调用端传过来放款方,再从字典中去取对应的规则类的实例,再根据配置的顺序来执行,返回最终的校验结果给调用端。


    下面看下提现服务中处理合同的方法,合同相关逻辑也定义成了一个模块,并且根据放款方 key 来取出对应的工厂类,比如外面工厂类 FoticModuleFactory ,它实现了模块工厂接口 IModuleFactory,调用其实现的 CreateContractModule 方法来实例化具体的模块类 FoticContractModule,当然它也是一个继承公共合同模块类 ContractModule 的子类,主要逻辑都包含在基类中,和提现服务基类模式一样,这里就不做介绍了。


大致就这么多。



标签:
分类: 设计模式

发表评论 没有账号,注册评论