在之前的文章中,本人提出了信息树的概念,并对其在实际项目中进行运用做了一些探索。其虽然有一定效果,但也只能解决部份问题,并非本人所想要的“银弹”。故本人再次思考与探索,是否存在一种简单、易懂、便于扩展,且能与代码紧密联系的设计方式。经过本人一段时间思考,探索出一中新的设计模式,可以符合以上要求,下面我先通过一个简单示例进行说明。
假设这样一个业务场景,用户可以通过PC或小程序注册,注册完后需要进行实名认证,按本人新的设计方式,可以将以上场景设计为:
在上面的设计图中,将用户的认证、注册看作目录(类似Restful),每个目录有子目录以及操作,在上图中操作为“PC注册”、“微信注册”、“认证”。“已PC注册”与“已微信注册”这两个目录互斥,用户不可能同时进行PC注册以及微信注册,就如同同一个电影不会同时出现在“最新上映电影”与“经典老电影”目录中。
以上的设计思想为,将某用户看作为一个资源,根据具体操作,移到某个目录下,如进行了任一注册操作,则将其移到相应目录下当然,我们可以对上面进行一下改动,改动如下:
在上图中,我们增加了“已注册”目录,在这个目录下有个子目录“通知”,其下有“短信”以及“邮箱”通知操作。以上说明用户注册完后,还需要同时短信和邮箱通知。并且在注册完后,将用户状态设置为1,即状态为1表明用户已注册。同样认证需要用户先在“已注册”这个目录下,即状态为1才可进行认证,且认证完后状态设置为2。
通过上图可知整体的用户业务流程,且已认证为用户的最终状态,其可根据实际业务变化,,调整目录结构。此种设计方式与UML想比较,涵盖了流程、状态变更、逻辑判断(可结合信息树,“可认证”即为之前所说的信息树中的其中一种信息)。
另其对于研发人员更友好,更与代码逻辑紧密结合,并可给后续代码文档化奠定基础,并更易于从业务角度编写代码。如我们需要编写一个查询已认证的用户,那么一般情况下是将status参数传值为2,进行查询,但其实可以如伪代码为:xxx.find(“已认证”)。至于今后其status状态2或是为3是已认证,都需要调整代码逻辑(此会在我的文档即编码的开源项目中实现)。
大家可能通过以上示例,有了一个基本概念,以下是我对其设计方式做了一个具体定义没具体如下:
1.目录
一个目录下可以有多个目录、操作、依赖信息、及状态转变。一个目录可同时存在于多个目录中。当某个数据或内容需移至此目录时,需满足:
- 其数据已经或曾已经移至过此目录的所有子目录下,除非子目录互斥,或有表明只需移至任一子目录中。如未移至子目录下,应先将其先移至所有子目录下,再移至当前目录。
- 需存在相应的依赖信息,如依赖信息不存在,则无法移动至目录
2.操作
对应具体的业务逻辑,可设置简单的计算逻辑及对应的数据操作
3.依赖信息
移动至此目录的前提条件,此对应信息树中的信息
4.状态转变
移动此目录后,某些字段对应的状态变化,可对应信息树中的信息
我们通过以上定义,再来看刚才用户的例子,如将要用户移动至“已认证”,则用户需曾移至“PC注册”或“微信注册”,以及“通知”。且如果移动至“已认证”发现未注册时,则会先进行注册操作,并移动至“已注册”后再进行认证,这等于同时进行注册与认证,用伪代码表示为:xxx.execute(“已认证”).start(“PC注册”)。
以下我们通过一个订购、支付的业务场景再进行说明,设计如下:
以上为整个订单的订购、支付、退款、取消流程,每个操作都有相应的状态变化,另我们看到所有的订单操作都需基于“有效用户”,即用户已完成认证。其中比较特殊的时“支付结果”,其有三个分支,分别为“支付成功”、“支付失败”,以及正在进行支付。
其展示了对于非功能性如何进行设计,如支付操作前,先将状态变为“支付中”,根据支付结果,可能为成功或者失败。同样对订单进行支付操作时,会先判断订单是否处于“支付中”、“支付失败”、“支付成功”任一情况中,如存在任一一种情况则无法进行支付。
同样对于已支付过的订单,会判断其是否已经退款,即此订单是否已经移至已退款目录下,如没有,则继续按路径搜索是否已支付成功,如搜索到则先进行退款操作,再进行取消操作。如果此订单没有支付,则换另一路径搜索此订单是否已订购,如已订购则进行取消操作。
用为代码可以表示为:xxx.execute(“已取消”),此会根据设计的路径,自动检索路径进行执行相应操作。通过此种方式,也便于在代码层面对功能进行设计划分,更易于写出高扩展性、通用性的代码。
如果我们想要搜索曾做过支付的订单,则可以xxx.find(“订单”).eq(“支付结果”);如果我们要搜索订购成功过的订单,则可以xxx.find(“订单”).strat(“已订购”);或者指定范围进行搜索xxx.find(“订单”).start(“已订购”).end(“支付成功”);从原来基于数值查询,变为面向业务查询,另如果查询时,需要相应的用户数据,则可写为xxx.find(“订单”).with(“用户”).start(“已订购”).end(“支付成功”);
之前本人提出的业务模型、数据模型已经在开源项目中实现(https://github.com/szlwin/doc-eq-code-Dgremlin/),后续会将信息树与本章的目录树也在此开源项目中实现,将四者结合起来,希望能打造出一颗银弹。