这里分享一个我在业务系统设计过程中常用的一个“复合模式”,用作一个在业务系统设计中运用“开闭原则”的例子。

背景

这是一个账务系统,负责处理各类业务流程中发生的若干个账户之间的转账相关逻辑,包括账户余额的变更、以及各账户的流水记录。
这个系统的复杂度在于:不同的业务流程,所需要操作的账户、金额的计算公式、以及流水的类型,都有很大的差异;即使是同一个业务,里面也会细分为多个子业务,账户、金额、流水类型又各不相同。而且,业务、子业务还会不断的新增和变更。这就要求我们在设计时,必须充分考虑扩展性。

思路

首先,业务流程虽然种类繁多,但是抽象、概括之后,其实核心逻辑非常简单。第一步当然是校验,然后是账户查找、计算金额等数据准备工作,准备好必要的数据之后就可以记账了,记账完成后做一些收尾的操作。
这样一来,所有的相关业务都可以放到这同一个“抽象”层次内来做处理了。如下图所示。

扩展性问题则是这样解决的。这个“抽象”层次内,最顶层的“服务提供者”只提供一个分发服务。它会根据外部传入的参数,选择对应的业务服务提供者,并将参数交给它来处理。
而“业务服务提供者”的最顶层,同样只提供分发服务。它会根据一些更细致的规则,选择对应的子业务服务提供者,然后将参数交给子业务服务提供者去处理。
这样的服务分发层级,在现实生活中可以找到很直观的例子。在一些大医院的门诊部大门口,会有一个分诊台。分诊台的护士会告诉你应该挂哪个科室的号。而在一些大科室的门口,又会有一名护士负责叫号,并指点被叫到的病人去找哪位医生。实际上,这也是我做这个“分发”服务的一个灵感来源。
在这个“分发”的框架下,新增一套业务是非常简单的。并且,由于真正处理业务功能的服务提供者只需要专注于自己的“一亩三分地”,与其它的业务之间很少发生耦合,因此,在必须对某些功能进行修改时,影响范围也会非常的小。
理论上,“分发”这个动作可以无限嵌套,因此无论多复杂的业务逻辑,都可以通过不断的分发处理来进行简化。只不过这样做会使得线程处理栈变得非常深,给理解系统和debug和trouble shooting增加不必要的麻烦。我们的系统做到二级分发,已经有“类爆炸”的困扰了。

类图


顶层的BizAccountEventService就是我们匹配“业务抽象”所建立起来的接口。所有的记账相关业务功能,无论是一级分发、二级分发,还是实际的业务功能处理,都是这个接口下的一个实现类。
只有两个类直接实现了BizAccountEventService接口,其中之一就是BaeServiceAsDispatcher,即业务抽象内最顶层的“服务提供者”——一级分发器。一级分发器需要一个服务工厂,以根据不同的参数提供不同的接口实现类。这个工厂既可以是一个简单的Map,也可以做得更复杂一些。我们单独创建了一个工厂类,这样可以方便地将一级分发扩展为二级分发。
在一级分发器中,服务工厂提供的类实际上都是二级分发器。二级分发器与一级分发器的逻辑实际上是一样的,也是利用工厂来根据不同的参数获得不同的接口实现。因而,二级分发器都继承自一级分发器,实例之间的差别基本上只是一个工厂。
直接实现BizAccountEventService接口的另一个类是BaeServiceAsSekeleton。这个类是一个模板类,它定义了真正的业务操作的核心步骤:校验、预处理、转账、后处理。所有的业务处理类,都必须是它、或者它的子类的实例。
但是,与教科书上的“模板模式”不同的是,BaeServiceAsSekeleton中的模板模式,并不是通过继承、而是通过组合来实现的。这个模板中的四个步骤,被分别委托给了三个接口(BaeValidator、BaeEditor、BaeTransfer)。这样做可以增加不同子业务之间的代码复用性。例如,业务甲的逻辑是ABCD,业务乙的逻辑是EFGH,而业务丙的则是ABGH。这种情况下,由于Java单继承的限制,业务丙无法通过继承甲或乙的类来完全复用代码,但是组合的方式却可以轻松做到。
关于这个接口,项目组内曾有过争论。有同事认为,应当为BaeServiceAsSkeleton类提供另一个接口,以便于保持BizAccountEventService接口的层次简单。这样做当然可以,不过我们没有把精力放在这件事上。

开闭

那么,回过头来看“开闭”。“对新增开放”很好理解,那么什么是“闭”呢?
实事求是的说,我们不可能完全地把“修改”关在门外。在这个“分发”方案里,我们也仍然需要通过修改分发相关代码,来增加新的业务、或变更原有业务。我们能够关闭的、也许也是我们真正想关闭的,是大量的、大范围的修改代码、是这样做所带来的不可控的风险。
以这个“分发”方案为例。当一种业务逻辑发生变化时,我们需要修改的仅仅是分发相关代码、以及这一种子业务所涉及的部分子类。这种修改的影响范围被牢牢锁定在业务抽象之下、甚至是单个子业务的相关抽象之下,因而,其中的风险是清晰明了的,因而也是可以有效控制住的。甚至于,我们可以保持原有业务代码完全变,而通过继承、多态等方式扩展出新的业务逻辑,从而用“废除+新增”的方式完全替代“修改”——当然,这种方式会带来很多冗余代码,值得商榷。
但是,把不可控的风险关在门外——我们做到了。

本文转自 斯然在天边 51CTO博客,原文链接:http://blog.51cto.com/winters1224/1959651,如需转载请自行联系原作者

业务系统中的开与闭——分发模式相关推荐

  1. 业务系统中的Word文档如何转成pdf

    在业务系统中Word文档转pdf是常见的需求,Word文档格式用于业务文档的编辑过程,Pdf文档格式用于流转分发的过程,通过zOffice的Open API可以方便的把Word文档转成Pdf,同时可以 ...

  2. 微信openid 服务器备案,微信生态中的 openId、unionID和业务系统中的ID

    在进行微信生态相关的开发的时候,经常会遇到一个术语:openID.openID 在微信生态下几乎无处不在:你想要识别用户身份?需要 openID 你需要给用户推送消息?需要 openID 除了 ope ...

  3. 在业务系统中寻求技术含量

    从进入互联网公司开始工作起,每个人都在问自己,CRUD 到底有什么技术含量? 别觉得 CRUD 只是业务工程师的问题,无论你在写什么程序,基本上都是在和数据打交道,除了读就是写.只不过读写的时候还会附 ...

  4. 设计模式在业务系统中的应用

    简介: 本文的重点在于说明工作中所使用的设计模式,为了能够更好的理解设计模式,首先简单介绍一下业务场景.使用设计模式,可以简化代码.提高扩展性.可维护性和复用性.有哪些设计模式,这里就不再介绍了,网上 ...

  5. BPM平台在企业业务系统中使用的价值讨论

    1. 企业应用系统的分类以及BPM这种平台分别在这两种不同类型系统中的作用 因为在系统建设之前,我们要分清楚,准备建设的系统属于那一类 ,目前企业应用系统大体我觉得可以分以下几类 A)  垂直的业务系 ...

  6. 从2开始,在Go语言后端业务系统中引入缓存

    本次我们接着上两篇文章进行讲解<从0开始,用Go语言搭建一个简单的后端业务系统>和<从1开始,扩展Go语言后端业务系统的RPC功能>,如题,需求就是为了应对查询时的高qps,我 ...

  7. 状态机/流程引擎/审批流的流程引擎/结合低代码开发的流程引擎 区别 业务系统中使用流程引擎

    理念 - 反对用模板,用流程引擎实现业务 先强调一点. 业务系统, 要学习 ,反对用模板,用流程引擎实现业务. 除非有人参与,必须用流程引擎,不然不要用状态机or流程引擎, 不要用. 但是要学习流程引 ...

  8. 架构设计 | 分布式业务系统中,全局ID生成策略

    本文源码:GitHub·点这里 || GitEE·点这里 一.全局ID简介 在实际的开发中,几乎所有的业务场景产生的数据,都需要一个唯一ID作为核心标识,用来流程化管理.比如常见的: 订单:order ...

  9. 英语中的开音节,闭音节

    一.音节:音节是读音的基本单位,任何单词的读音,都是分解为一个个音节朗读.在英语中元音特别响亮,一个元音可构成一个音节,一个元音和一个或几个辅音音素结合也可以构成一个音节.一般说来,元音可以构成音节, ...

最新文章

  1. 14个顶级开发社区 [程序员]
  2. 【转载】一行代码加载网络图片到ImageView——Android Picasso
  3. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---02
  4. nodejs--http模块
  5. c++bisection method二等分法的实现算法(附完整源码)
  6. java abstractrequest,Java AbstractJackson2HttpMessageConverter類代碼示例
  7. 仿iphone触屏手机界面
  8. 如何避免重要需求遗漏?
  9. win7中配置iis
  10. 9.2NOIP模拟题
  11. 堆排序代码详解(Java实现)
  12. cesium加载天地图矢量地图设置为暗黑系风格地图【独门秘方】
  13. ipad编写python_ipad怎么写python
  14. 解决VS停止调试时浏览器自动关闭关闭浏览器时自动停止调试
  15. 计算机windows7桌面是指什么,认识Windows 7桌面,增加了哪些功能,它是应用程序操作的出发点...
  16. 我如何用Unity3D实现一个Galgame框架(存档读档与设置面板的思路)
  17. 折弯机使用说明书_折弯机使用操作方法-几大要点
  18. Baumer工业相机堡盟工业相机如何联合BGAPISDK和佳能EF变焦镜头实现相机的自动变焦(C++)
  19. 「计算机日常」笔吧测评室笔记本测评科普视频笔记
  20. 靠着群友的接济,一毛不拔的学会了Python!(学习路线+资料分享)

热门文章

  1. 第一位女程序员奥古斯塔·爱达·金
  2. 得胜php100怎么样,优缺点爆料测评得胜dm2300和得胜ph120区别怎么样?哪个好?真相评测揭秘...
  3. xbox 0x_Xbox Series X之路
  4. Android 手机按键客制化详解
  5. 求图纸形心的数据预处理与形心计算代码:输入多个矩形的坐标和长度宽度,计算它们的形心坐标
  6. 前端----高度塌陷及解决办法(最详细解)
  7. USACO 1.5.3 SuperPrime Rib 特殊的质数肋骨
  8. 云服务器竞争,比格云将以“黑科技”开启云服务器竞争新格局
  9. 西班牙插画师 Alex Vede
  10. WPF中Popup控件的使用