基于链式UI流程的业务模块设计
研究背景
北斗项目的某些页面中,有非常复杂的交互逻辑,页面上同时存在数个UI控件(系统默认的checkbox,radio,select等),并且这些控件根据业务逻辑都需要互相关联,比如:radioA当用户选中的值是1的情况下,那么checkboxB要被禁用;checkboxB被禁用的时候某些页面元素要被隐藏了,同时radioC要被禁用……诸如此类。这些关联随着业务逻辑的复杂可能会千丝万缕,纷繁交错,开发起来也会有很多周折和反复。
分析过程
如何理清各个模块间相互影响的流程,找到他们可抽取的共性?我想到了“格局”这个词。
例如,井字棋的之间的关系是由比赛规则决定的。通常,这个关系不是线性的,因为从一个棋盘格局可以派生出几个格局,例如如图所示的格局可以派生出五个格局,而从每一个新的格局又可派生出四个可能的格局。因此,若将从对奕开始到结束的过程中所有可能出现的格局都画在一张图上,则可得到一棵倒长的“树”。“树根”是对奕开始之前的棋盘格局,而所有的“叶子”就是可能出现的结局,对奕的过程就是从树根沿树叉到某个叶子的过程。
同理,北斗的个性化报告定制页面是个复杂的表单提交页面,用户可以根据需求使用同一个界面定制不同类型不同信息粒度的报告。所以页面中很多交互控件,每个控件会有不同的状态,这样所组成的不同的界面就可以用格局的思想来理解。如图:
高亮的区域代表可以和用户发生交互的模块,红色箭头表示当某个UI控件发生改变时会引起其他控件变化的关联关系。
每当用户与一个UI控件发生交互,这个控件被改变后的值将引起他所关联的UI控件再改变,由此一直链式反应下去,催生一个新的格局。在开发中需要关心的是如何自动的得到这些格局,既然是由链式反应生成的,那么逆推上去,就需要控制这些链。
先对UI模块进行抽象:
- 每个UI模块都包含至少一个UI控件,如radio,checkbox,select等;
- 同时这个UI模块对用户与UI控件的交互进行控制,即对处理用户点击等事件的界面响应;
- 另外需要有接收外部数据加载后的处理; 这是一个UI模块最基础的部分,之后要为UI模块之间的关联关系做一些准备:
- 当UI控件的值被改变,不管是用户点击还是数据加载,会向关联的UI模块发出改变的命令,同时传递改变的值;
- 要知道和其他哪些UI模块关联,就需要一张表来记录,同时还需要一个向这张表添加对象的注册方法;
- 当一个UI模块接收另一个UI模块改变命令的时候,还得告诉这个UI模块是谁带来的改变,以便选择相应的处理,就需要一个标识变量;
这样就基本完成了UI模块类的描述,可以抽象成下面的UIModule类图:
+ moduleKey : String |
模块标识串 |
+ linkedUISet : Array<UIModule> |
相关联的UI模块集 |
+ onLinkedUIChange : Object<Function> |
建立关联后记录的关联处理映射表 |
- ui : UI |
模块内具体的UI控件 |
* changeUI(config: Config) : void |
接收由数据发生改变的处理 |
+ linkWith(ui: UIModule, fn: Function, linkMap: Object) : void |
与其他模块建立关联的方法 |
+ changeLinkedUI(value: String|Number) : void |
驱动与自身关联的UI模块改变的方法 |
接下来就要在实例化的各个模块间建立关联。建立关联的函数linkWith
有三个参数,分别代表:ui
-与谁建立关联;fn
-当关联模块发生改变时的处理;linkMap
-变化时应用相应值映射的配置。
这里的配置是一个映射表,由发生变化的UI模块的发送值映射到被关联模块相关处理要使用的参数集,例如:
报告类型选择模块中,每个radio的值对应一个参数集,不同参数集的数据结构是相同的,其中配置了用户可否配置分网站选项,可否进行深层推广组的选择等信息。
归纳一下,就是每个要引起其他UI模块改变的模块,都需要定义一个由自身可能出现的值对应的配置集,配置中包含了被引发变化的UI模块所需的参数及不同的值。不管关联数是否大于模块数,配置表的个数是不会大于模块数的,即可理解为配置表依赖于模块,而不依赖与关联,但又可以包含关联所需的信息。
如果用硬件构成的思想来看这个系统,那么每一个关联,可以看作是同类型的逻辑门芯片,有输入输出,同时这个通用的芯片上有一个可扩展的插槽,真正使用时的输入-输出映射就由插槽中的配置模块提供。准备好了这个关联芯片后,就用导线把他连接在两个会发生关联的UI模块之间,就建立了一个关联。当上游UI模块被用户改变发出信号后,关联芯片根据输入和模块配置自动计算出一个输出,并传递给下游的UI模块,驱动他引起相应变化。
如下图所示,绿色的是UI模块,虚线框包围的就是关联模块,其中黄色的部分就是通用的关联芯片,白色插入的就是关联配置模块,以构成完整的关联输入输出。任何两个UI模块要建立关联,就在他们之间连接一个关联芯片。当UI模块和关联都越来越多的时候,虽然逻辑关系可能变得复杂,但仍可以画出清晰易懂的模块结构图。
上图表示的是UI模块A在发生内部改变的时候,会激发UI模块B和UI模块C发生相应变化,同时UI模块B发生变化的时候,还会激发UI模块C发生变化。
进阶思考
当需求的逻辑越来越复杂,元素越来越多的时候,可以建立一张模块关联表:
主动模块 \ 被动模块 | A | B | C |
---|---|---|---|
A | - | 关联:A-B | 关联:A-C |
B | - | - | 关联:B-C |
C | - | - | - |
表格内容同样说明了上图中A,B,C三个UI模块的关联关系。
目前程序上的处理是简单的实例化一些模块,再建立一些关联。如果对此块的逻辑进行优化,可以用一个表来进行管理,而实例和关联关系都作为配置项放入表中,以使整个模块关联关系的建立自动完成。
总结
针对基于复杂的UI交互流程的页面,特别是这种“联动”效果很多的页面,通过对逻辑的分析,从具体模块的共性中抽象出了一套较为通用的模块设计模式,并在项目中得到了具体的实现,为类似产品的前端设计提供了一个参考的思路。
-EOF-
永久链接:https://yanjunyi.com/blog/posts/business-module-solution-based-on-chaining-ui-flow-design