开发小结-业务管理类

本篇文章关注于编程实践中的相关流程设计内容,内容来源本身过去的工做总结。编程

业务流程设计

越复杂流程,越容易出错。为了减小出错的状况,须要提取而且封装通用逻辑,用一个易于理解的名字来对外提供服务。在业务不一样抽象层级上,干各自职责对应的事情。缓存

先后台交互的流程越多,须要维护的状态就越多,出现问题的几率就越大,所以在不影响主要功能的前提下,流程能简化就尽可能简化,那些被简化的路径,在某些异常场景下,会对业务有必定的影响,具体影响到什么程序,在上线前,谁也很差说。可经过数据埋点获得上线后的真实数据,以供权衡。网络

针对复杂业务时,不能一上手就开始写,要先设计好框架和总体流程。对于一些需求上面没有明确的交互细节,通常按照通用交互细节来就能够。框架

对于具体子功能的实现上,须要细致考虑,较好的实践方法是逐条列出来,在每种状态或状况下,什么样的输入获得什么样的输出。函数

在复杂程序执行过程当中,须要维护多个状态,随着业务的改变,在处理于状态关联的事件时,思惟负担会愈来愈重重,很容易顾此失彼。
为了更好的控制复杂度,简化逻辑,须要对状态进行分级管理,将每一个级别的状态和对应处理分层化。对外统一状态操做接口,每一个模块中,都经过统一状态接口来管理状态。测试

每个常量数字,在业务上都要有依据来源,要么是经验值,要么是业务规则规定的。字体

失败流程

任何失败重试类操做,都要考虑对后台的影响。由于网关只会转发请求,大量冗余的请求会给后台形成巨大压力,可能会影响正常业务流程。对于失败重试,必定要设置定时间隔以及重试次数,这里的失败要区分类型,若是是业务操做类的失败,业务层直接提示出错便可。若是是其余类型的失败,底层须要作好重试策略,直到重试策略返回失败,才可认为是失败,此过程对上层是透明的。优化

针对开发回发的数据,要进行有效性校验才能继续往下走,在有些业务场景中,须要对不一样类型的失败分配不一样类的错误码,以便外部处理。好比读取配置信息这个功能,可能的出错就有如下几种状况:this

  1. 配置文件不存在
  2. 配置文件存在,但文件格式不正确
  3. 配置文件存在,文件格式正确,属性Key缺失
  4. 配置文件存在,文件格式正确,属性Key正常,属性Value缺失
  5. 配置文件存在,文件格式正确,属性Key正常,属性Value是异常值(超大的数,或者是负数)

若是由于上述种种缘由,致使读取配置文件失败,那要作好默认配置的处理,根据产品要求,对不一样的异常状况,作出对应的处理,使用默认配置继续运行,仍是弹框提示用户等。设计

在对后台返回来的格式化数据进行解析时,必定要考虑对应字段不存在的状况下该如何处理。无论后台返回什么类型的错误数据,都不能崩溃。

流程优化

仔细分析业务流程,避免没必要要的依赖和操做,相关的业务逻辑,要聚合到一块儿。

分析问题的思路要不断在实践中打磨,反思,总结。本身第一时间想到的方案,每每还有很大的优化空间,那么,从什么角度去思考优化呢?在开发实践中,逐渐明确两个大的方向,在此记录一下

  1. 从底层往上层思考 : 底层是最基础的数据层,业务流程的数据是否是能够往下移动到底层,而后调用底层明确性语义接口函数来实现上层业务,而不是把一堆判断逻辑都交由业务层去作。

  2. 从上层往底层思考 : 在业务层和底层数据之间,要有一个间接层,对上提供较为高级的功能,对下封装最底层数据,提供更高层次的接口。

  3. 类似的业务处理采用类似的办法,不要一堆条件,范围A-C的用这样的处理方法,业务范围D-F的用那样的处理方法,其余类型业务用其余的处理方法。这样一来,业务和对应的错误处理分散在各处,后期维护很容易出错。

一个类,如需向其余类得到相关信息,不要依赖于类与类之间的继承组织关系,尝试经过this来进行强制转换来获取,由于这种层级关系在将来可能会发生变更,而这种层级关系的变更,不会通知使用者,所以在实现相似需求时,须要将对外部的依赖关系尽量的下降,建议采用消息的方式,每层规定好消息的职责,一层一层向上传递请求,直到某一层给与响应为止。

一致性

一致性,指的是对于UI元素和代码命名的一致性,同一逻辑元素的命名一致性,它在UI层、中间层、网络层以及后台接口中的核心词汇要保持一致,这样增长代码的可读性。

一致性体如今资源管理上,同一份资源,谁申请,谁就负责释放。谁增长引用记数,谁就负责减小。

在一些通用功能的设置上,保持代码一致性很重要。好比设置控件字体,原生系统会提供一套接口,自绘部分也会提供一套接口,那么外部在使用时,就会有疑惑,设置字体是用原生接口仍是自绘接口呢?他们会不会相互影响呢?在前期设计时,同一类功能,只提供一套接口较好。

类似操做可以归集到一块儿的,必定要归集到一块儿,让逻辑聚合,而不是处处分散。

修改影响评估

好比,你修改了A函数中的A分支,A函数的B分支没有改动。外部O1模块使用了A函数的B分支,O2模块使用了A函数的A分支,那么你的本次修改涉及到的影响范围是哪些呢?O2模块确定是要测的,O1模块需不须要提测?答案是须要的,从广义上来看,任何调用A函数的模块都要重测一片。即便本次修改没有涉及到分支,也要去测试。

在尝试修改后台程序时,要注意与以前系统的兼容性。

在修改一个涉及范围比较大的问题时,若是涉及的地方太多了,为了最终问题的解决和测试方面的平滑过渡,须要将问题分治,按照功能需求模块来统计汇总,而后,将问题列表按模块分配给各自负责人,由他们去进一步往下分散进行修改,这样能够保证大型问题的平滑过渡,为开发和测试提供缓冲时间段。

好比本次修改点可分为如下几个集合,

集合1:涉及到哪些方面

集合2:涉及到哪些方面

集合3:涉及到哪些方面

逻辑完备性

对于层级调用,资源的申请和释放,要特别注意一致。这一点,特别容易遗漏。
好比说,先压入缓存,再发出请求,那么,清理缓存的时机,应该要覆盖发出请求后的每一条执行路径。
7.1 发出请求失败
7.2 发出请求成功,响应成功
7.3 发出请求成功,响应失败

一段业务代码,正确的路径只有一条,但错误的路径有不少条,怎么处理在这个过程当中有可能出现的错误,就很显的功力所在了。

若是遇到一些须要特殊处理的地方,优先把大部分状况放在主干逻辑上,在额外的分支中处理额外。

凡是涉及到网络请求的,若是中途关闭了页面,必定要注意请求资源的清理操做,这样在从新打开页面,不会收到上一次无效的响应。

优化前的准备

没有profile的优化都是瞎扯淡,一旦开始重构,就要指定好优化目标,全部的相关修改都聚焦在目标上,不能跑偏了。优化前要分析现状、制定方案和可量化的目标,完成优化后,须要提取相关的数据,总结对比可量化的分析优化成果。后台重构须要配置A/B方案,对于可能会有变化的模块或者业务,或者A、B方案很差取舍的,可采用后台配置采用哪一个方案,当重构的版本上线时,先保留旧的模块,若是有问题,能够在后台中配置切回到旧的模块,这种思想同能够同灰度发布一块儿使用(5W,10W)

在进行组件的重构时,须要遵循最小影响范围来作,一处重构,尽可能只影响到这个业务,不影响到其余业务,保证可控性和可测试性。

维护经验

对于接手旧项目,里面用到的第三方库,若是有新版本的接口库或者更好的第三方接口库,该如何抉择?

  • 若是原有的库可以知足需求,则不要贸然替换或者升级
  • 若是原有的库不能知足特定场景下的需求,优先本身封装在特定场景下的接口,以供使用。若是特定场景下的需求须要的新功能不能经过封装已有API来提供,而该功能在原有库的升级版中存在,那么须要和原有库维护者商量讨论后再进行升级。
  • 用新的三方库来替换,该方法改动范围会很大,不到万不得已,不容许使用。

当老旧代码中有不少相互交缠的逻辑,须要改动多处才能改好词Bug,很差评估改动影响的范围,这个时候,为了最大限度的兼容老旧代码,不能继续在老旧代码的基础上继续填坑、挖坑,若是选取其中一个简单的界面,用清晰简单的逻辑去实现,而后在逐步替换老旧模块,小步慢跑的改进。

当须要在原有功能上面增长新的功能时,要特别注意一点,就是原有功能所使用的指令和流程不能更改。好比查询A产品的天数信息,原有的指令只支持一种产品的查询,如今其余界面有同时查询多种产品的状况,而原有指令只实现了查询单个产品信息, 那么,下面有两种解决方法:

优势                       缺点
方法一:修改原有指令,使其支持多条信息的查询      增长指令的功能         需修改原有查询单条产品的页面,增长了测试成本
方法二:不改动原有指令,新增支持多条信息查询指令  不会影响已有功能       相似功能有多处实现,查多条包含查一条的功能

由于查询一条产品信息属于查询多条产品信息的特例,为了可维护性,采用方法一较好。

一个简答的权限判断功能设计

有A、B、C三个帐号,有ID1,ID2,ID3,ID4四个功能,每一个帐号容许的功能以下:

A--> ID1, ID3
B--> ID2, ID3
C--> ID4

如今要设计一个权限判断的函数,给定功能ID和帐号,返回是否容许执行此操做判断?

一种是从上往下,从入口ID和各个可操做ID段来判断,优势是直观,简单,缺点是对扩展性很差,将来要增长新的入口时,须要修改多出
方法一: 从上到下,逻辑简单直接,可扩展性差

bool IsAllow(Account acnt, int nFunId)
{
    if (A == acnt)
    {
        if (nFunId == ID1 || nFunId == ID3)
        {
            return TRUE;
        }
    }
    else if (B == acnt)
    {
        if (nFunId == ID2 || nFunId == ID3)
        {
            return TRUE;
        }
    }
    else if (C == acnt)
    {
        if (nFunId == ID4)
        {
            return TRUE;
        }
    }
    
    return FALSE;
}

另外一种方法是从下往上,将每一个交易ID和可以操做条件集合在一块儿,这种方式的扩展性和可读性都很是好,推荐使用。

struct TIDInfo
{
    int nId;
    vector<Account> vAllowAcntType;   //  该ID要求的帐号属性
    
    bool IsAllow(Account acnt)        // 给定帐号是否存在特定属性
    {
       return vAllowAcntType.find(acnt) != vAllowAcntType.end();
    }
}

vector<TIDInfo> allIdInfo;            // 全部功能ID集合
bool IsAllow(Account acnt, int nFunId)
{
    for (int i = 0 ; i < allIdInfo.size(); ++i)
    {
        if (allIdInfo[i].nId == nFunId)
        {
            return allIdInfo[i].IsAllow(acnt);
        }
    }
    
    return FALSE;
}
相关文章
相关标签/搜索