成为工程师 - 搭建系统先搭建框架

开发 架构
对于流程引擎,我们只是给出了一种最最基础的实现方式而已,但是对于很多系统来说,这么设计已经足够了。事实上,真正强大的流程引擎还包括【分支循环】【异步化】【可视化界面管理】等各种高阶功能,你可以自己做一些了解。

作为工程师,日常的工作基本上都是围绕着【系统】展开的。【搭建一个系统】是工程师必须具备的最基础能力。

从业至今,我自己负责过很多系统,也看到过很多系统。有的系统搭建得非常优雅,无论是可读性还是扩展性都非常好。说白了就是代码看起来清晰干净,研发起来快捷且安全,排查问题也容易定位。但还有一些系统你就是看上好几遍代码都捋不清逻辑,改造的时候更是无从下手。

一个系统存在复杂的业务逻辑是正常的,而一个优雅的系统是能够通过良好的结构去管理这些复杂性。我把这个结构称之为【系统框架】。

搭建系统框架是系统建设的第一步,也是最重要一步。我们这篇文章就来聊聊如何搭建一个好的系统框架。

我们先看一个反例:

反例时间

假设我们有一个http接口,需要返回用户的信息。用户信息包括:用户昵称、用户vip等级、用户标签、用户余额、余额历史以来充值总额、用户最贵一次消费。

下面的代码是一种典型的实现方式(可以看注释来了解步骤):

图片

很多同学看到这样的代码觉得已经挺好了。有日志、有注释、有异常处理,代码也没有挤在一堆。但真的是这样吗?

我认为这样的代码确实反映了工程师一定的技术素养,但起码存在以下这些问题:

【Q1】如果新增接口,所有的日志打印要冗余写一遍,包括入口日志、出口日志、异常日志。

【Q2】如果新增接口,try-catch的异常处理逻辑也需要冗余重写。

【Q3】如果新增一个只获取用户金额信息的接口,需要冗余复制上述代码中和金额相关的部分。

【Q4】如果接口需要修改,返回新的信息,那就需要往这个代码里添加新的业务逻辑。而这个类一旦有变化,就涉及对这个类的回归验证。

【Q5】如果我要同时支持可以根据用户昵称来搜索用户信息,那么我要新增一个基本完全一样的接口(除了入参不同)。

(记住以上这些问题,我们下面会逐一来解决)

所以,如果用发展的眼光(需求新增)去看这段代码,你可以基本判断以后会存在大量的逻辑冗余。

大量的冗余会带来研发的低效、升级的遗漏、逻辑不一致的风险等等。

此外,不同工程师可能都有自己的编码习惯,同样是处理日志,异常,写法可以迥然不同。

结合在大厂多年的经验,优雅的系统会结合两种设计方式来解决这类冗余的问题。我们一起来看看。

一、模板模式

【模板模式】就是设计模式中的“模板方法模式”。模板方法模式的核心思想就是:统一算法框架,暴露算法要素给子类来实现。

看定义还是抽象了一些,我们直接看例子。

下图就是一个定义了算法框架的抽象模板类:

图片

可以看到,process方法里仅做了两件事:

【1】实现了所有接口的共用逻辑。比如打日志、计算耗时、捕获异常并处理。

【2】确定了步骤(步骤也称之为算法框架)。比如先校验参数,后执行业务逻辑。

基于上面的模板,我们的服务只需要做如下实现就可以了:

图片

下面我们根据【模板模式】的思想修改我们的反面案例,我们的代码就变成了:

图片

可以看到,通过模板模式,你起码会有这样几个好处:

【1】每个接口都不用担心忘了执行必要的公共逻辑,例如打印日志、异常处理。

【2】不用担心接口有遗漏步骤及搞错步骤顺序,例如入参校验在执行业务流程之前。

【3】接口只需要关心自己业务逻辑的实现即可。

【4】所有接口打印的日志及异常处理方式确保是一致的,方便监控和定位问题。

【5】如果需要增加一些公用的能力,例如埋点上报某个统计平台,只需要在框架中添加逻辑,所有接口都直接生效。

我们使用【模板模式】解决了业务无关逻辑的冗余问题,也就是上述针对反面例子提出的问题中的Q1、Q2,下一步我们要动手解决业务逻辑冗余的问题,也就是针对问题Q3、Q4、Q5。

二、流程引擎

流程引擎的核心思想是:将要执行的逻辑看成是一个个步骤的串接,由统一的角色来管理步骤的执行顺序,这个角色就是流程引擎。

我们用两张图来对比下使用流程引擎和常规瀑布式编码的不同。

1.流程式编码 vs 瀑布式编码

图片

上图分别展示了两种编码法方式【瀑布式编码】和【流程式代码】。

【瀑布式编码】就是从上往下按照步骤把业务逻辑写完。

【流程式编码】是先把可以独立的功能抽成一个个执行器。不同的服务根据自己功能的需求来串接这些执行器。

两者对比,流程式编码有这样一些好处:

【避免冗余】:同样的业务逻辑只有一份代码。

【最小修改】:如果需要加一个环节,只需要新增一个处理器,并且编排到流程中即可,对已有代码没有任何侵入。

【方便追踪】:我们可以在每一个节点执行完以后,在流程引擎中添加一些日志,以此来追踪执行过程。例如在哪里中断了?哪个执行器耗时最长?

【利于分工】:每个处理器约定好职责就可以独立开发,并且可以独立测试。

【可读性好】:流程式代码往往在一处编辑所有的步骤,代码可读性佳。看到一个流程由哪些节点组成,基本上就了解大概的逻辑了。

【灵活多变】:流程式编程还可以支持各个处理器以分支和循环的方式组合。

下面我们就来实现一个简单的流程引擎,并用它来继续改造上面的反例,以此来说明【流程式编程】的思想和好处。

2.处理器设计

我们先看下处理器的实现,处理器是被我们抽取出来处理一块业务逻辑的单元。如下图标识

图片

在反例里,我们可以抽取三个处理器。【用户信息处理器】【金额处理器】【消费记录处理器】。处理器接口如下:

图片

细心的同学可能会问,ProcessRequest和ProcessContext是什么?

【ProcessRequest】:对请求信息的封装。例如用户的userId、用户的客户端信息(IOS、安卓、以及对应版本号)、要求转账的金额、转账对象等。每个处理器都能够获得这些信息,根据自己的需要去使用。ProcessRequest中所有的值,原则上不允许被修改,以免原始请求信息被污染。

【ProcessContext】:流程执行的上下文,用于存放整个流程执行过程中的数据。在所有执行器处理完以后,结果组装器可以从ProcessContext中获取到各种结果数据,构造返回结果。

接着我们基于Processor接口,实现三个具体的处理器:

图片

图片

图片

我们处理器就搞定了,等着流程引擎来唤起他们吧!

3.流程引擎设计

下面我们来看流程引擎的设计,如下图标识,可以把这些箭头的控制理解为流程引擎。流程引擎的核心作用就是控制处理器按照指定顺序执行:

图片

下面是流程引擎接口:

图片

流程引擎只有一个start接口用来启动流程。

以下是流程引擎抽象类。抽象类除了实现对处理器执行的控制外,还可以包括日志打印、异常处理等操作。

那一个流程引擎需要执行哪些处理器呢?这由子类决定,子类通过实现getProcessors()抽象方法来指定使用的处理器。你看,这里又有模板模式了是不。

图片

下面看下我们具体的引擎子类是怎样的:

图片

可以看到,引擎子类实现getProcessors()方法即可。此方法就是告诉流程引擎具体要执行的执行器列表及执行顺序。

如果你走读代码到这里,看到list里放的三个处理器名称,你基本上就知道“用户查询接口”提供了怎样的功能。这就是良好的可读性。

试想,如果有一天,一个流程中需要新增一个逻辑,我们可以包装一个新的处理器,然后添加到上图中的processorList中即可。

每个接口都可以实现一个如上截图的引擎子类,用以编排需要执行的处理器。

4.主入口的改造

当引入流程引擎后,我们的主入口(controller)就可以改造成如下这样(我们附上和之前两个版本的对比图):

图片

你可以很明显的看到在改造之后,由于业务逻辑被内聚到一个个处理器中,入口处的代码变得简单清晰。同时你再也不用害怕每次业务需求都要改这个类,从而变得的膨胀不堪。

5.流程引擎设计一览

我们已经看到了整个流程引擎的实现过程。最后我们再用一张类图来一览整个设计,相信会帮助你更好地了解这种设计方法:

图片

今日总结

今天,我们正式进入到【成为工程师】的细节内容。我们提到,一个工程师最基础的能力就是搭建系统。而一个系统要搭建得好,首先就要有一个好的系统框架。

我们先是通过一个反例来说明了典型的瀑布式编码存在的问题。继而讲了通过模板模式和流程引擎两种设计方式来优化瀑布式设计。以此让系统的扩展变得”容易、安全且规范“。

对于流程引擎,我们只是给出了一种最最基础的实现方式而已,但是对于很多系统来说,这么设计已经足够了。事实上,真正强大的流程引擎还包括【分支循环】【异步化】【可视化界面管理】等各种高阶功能,你可以自己做一些了解。

不过,流程引擎的选择需要结合实际情况,不然也会引入额外的复杂度。

建议你收藏这篇文章,当你碰到系统设计问题的时候,可以回头来看看,相信可以帮助到你。

下一章我们会接着讲系统设计方面的问题,来讲讲一个系统要如何分层。

加油吧,未来的架构师们!

本文转载自微信公众号「 CodingBetterLife」,作者「 赵志强 」,可以通过以下二维码关注。

转载本文请联系「 CodingBetterLife」公众号。

责任编辑:武晓燕 来源: CodingBetterLif
相关推荐

2009-03-20 09:32:52

系统集成工程师素质

2013-06-26 10:34:56

工程师?谷歌

2021-03-24 15:15:34

数据工程师开发工具

2018-08-03 15:47:00

iOS框架开发

2015-12-09 09:03:22

2016-12-13 10:07:50

JAVA框架搭建

2015-12-09 14:37:30

2021-01-18 09:00:00

人工智能机器学习工程师

2018-03-29 11:23:25

IT人员云计算工程师

2016-01-28 11:18:09

卓越前端工程师

2015-08-24 09:02:49

前端工程师

2015-08-17 10:32:06

前端工程师优秀

2021-03-23 10:04:55

数据工程师工具数据分析

2015-05-20 10:02:02

程序员全栈工程师

2013-03-04 09:55:39

工程师软件工程师

2019-06-04 08:09:39

物联网工程师物联网IOT

2021-01-31 17:36:07

前端工程师职位

2022-07-22 09:55:29

软件工程师

2009-03-19 10:21:35

微软工程师职业发展

2021-03-09 10:24:46

数学计算机系统架构师
点赞
收藏

51CTO技术栈公众号