复杂表单应用解耦,淘宝机票订单实践

标签: 交互设计 表单设计 | 发表时间:2013-06-18 12:14 | 作者:dream
出处:http://www.boxui.com

在web应用中,复杂表单这类web应用富交互元素多,业务逻辑复杂,犬牙交错,且需求变化频繁。及容易成为晦涩和幽暗之地,也经常是各种代码坏味道的来源。针对这种典型的复杂应用,本文以淘宝机票订单为例提出一种架构模式梳理和消化表单带来的复杂性。

模块和组件划分

解决复杂表单的的第一步,划分模块。

概念上,为了复用和解耦方便,应将模块按照功能的内聚程度进行划分。强相关,频繁沟通和交互的功能应该归为一个模块。模块间尽量不存在依赖关系。也就是常说的“高内聚,低耦合”。 如下图所示,淘宝机票订单页面主要有被分为7个主要模块。

模块划分完毕,下一步确认组成模块的组件。 关于模块和组件的区分。一般按照以下三个纬度考量。

  • 是否有业务逻辑参与。
  • 是否包含html。
  • 是否具备一定独立性。

模块”,定义为一个包含”html”、”css(图片被认为是css的一部分)“、”javascript”的代码集。模块的应用方式多为通过web模板技术(如:velocity、freemarker、php)。因为包含了html,使得模块必须通过服务端合并加载并且最终推送到用户浏览器。此外,“模块”还是具备一定独立业务和交互的集合,最好可以被其他页面引用。良好的独立性也可以帮助协同开发,在实际开发中可多人可以并行开发多个独立模块,提高效率。

组件”,定义为一个仅包含”css”和”javascript”的代码集。正因为不包含html,所以组件可通过javascript异步加载。因为这种可异步加载的特性,组件在复用方面的容易性远超模块。组件没有业务逻辑或者仅有少部分公共业务逻辑。业务逻辑越多,组件的可复用性就越低。

模块、组件间通讯

组件/模块划分的目的是将彼此间相对独立的功能分离,前面通过模块和组件的划分解决了分离问题。实际中,模块之间存在协作关系。模块间应以一种轻量的方式协作。一般的为了更好的分离和解耦,可以考虑用广播的方式在模块间沟通,考虑使用事件的方式在组件间通讯。

如下图所示,淘宝机票订单页面的数据流向。

不同模块在后期均有可能扩展小功能。例如不定期的活动优惠等。事件广播可以让不同模块/组件间新增功能影响面缩小。在淘宝机票订单中应用中,使用广播组件通讯主要用来完成以下意图。

1、知会。 知会的特性在于异步通讯。广播发起方只需要放出事件,无需等待其他关注者完成处理。称为异步广播。例如表单模块的内容变更需要知会到显示订单金额的模块,显示订单金额的模块接受事件后需要更改金额。 基于这种方式的通讯,各模块之需要做好自己的事情,外部关注的事件广播出去即可。异步广播还有一个好处是系统坚固性比较强,广播发送者不会因为事件监听者的使用不当而异常。

2、请求数据 例如,模块6(负责提交)需要在被点击后从模块2(乘机人表单),模块4(联系地址)、模块7(金额计算)。获取具体数据提交。请求数据的场景特性在于,广播发起者需要等待事件处理者完成处理后再继续下一步行为。称为同步广播。

基于此机制。提交模块只需要负责综合校验,浮层,网络请求及异常处理。而具体请求的内容由其他模块决定。对后续模块的扩充起到了很好的左右。

复杂组件拆分

模块和组件划分完毕后,可能会发现某些组件非常复杂,几乎占据了整个web应用一半以上的代码。这部分组件由纯js实现,并且使用javascript模块加载器加载。 同一个组件大量代码纠结在一起,最终还是会导致架构腐化。因此,复杂组件需要进一步拆分。在淘宝机票订单中,乘机人信息组件是一个复杂组件。如下图所示:

拆分这类输入型的复杂组件,一般来说有两种思路方式。

  1. 纵切,组件树型式。 将组件进一步划分为更细力度的输入组件,将每个输入域作为一个单独组件。最终形成一个组件树。

这样的组织方式结构严谨层级清晰,最大的优点是很容易支持字段扩展。 但考虑如下场景,为了尽量友好的提示用户,需要在输入域外的某处增加提示帮助。

这种场景下组件树的组织方式每次在面对变化时就会略显手忙脚乱。难道把每个地方出现的tip都座位独立组件看待吗? 字段级的适普性降低了适应细节调整的能力,付出的代价在于界面体验。

  1. 横切,AOP式。 将所有输入域抽象的看待为同一个组件。按照组件的富应用特性分层看待。在本例中,乘机人组件被按照从简单到复杂分为3个切面。

切面1-基础展现层只负责最基础的可完成输入的表单控件,及基础dom管理。 切面2-富展现层负责修饰base层的基础html控件,形成富输入控件。 切面3-校验层负责对base层的输入数据进行业务级校验。 未来,如果新增tip或者其他业务逻辑,增加一个新切面即可,完全或者很少需要修改老代码文件。

淘宝机票订单采用了AOP这种方式,从最终代码量上来看,可以看出复杂度被比较均衡的分布到不同文件中去。

同样,这种方式也有局限,如果需要扩展字段,那将是一个灾难,你有可能需要到每一个切面里面去做修改。

有句老话说的好,没有最优方案,只有最适合的解决方案,任何解决方案,都需要放到具体场景中去评判。事实上,对这个问题的进一步研究,可以发现以下规律。

对于一个组件、模块,同时追求简单设计、适普性(字段级扩充)、界面体验是不可能的。如果场景需要适应字段灵活扩展,那就采用纵切的模式。如果使用场景需字段确定,需要更多细节控制力度,那就横切,AOP式。如果两者都要兼顾,就需要引入复杂设计,综合运用横切和纵切。但是这样形成的最终设计会很复杂,开发和可维护性上会有代价付出。

对于淘宝机票这类互联网应用,使用了横切的方式来拆分组件,因为在这个场景中,字段的数量是相对固定的,而围绕固定数量字段的优化需求是层出不穷的。然而在企业内网应用或者网站后台web应用中,字段的变化会比较频繁。建议主要采用纵切的思路划分。

表单校验

有表单的地方就有校验。项目初期,校验的功能总是不起眼。等待项目后期时候经常会发现校验已经占据了巨大工作量并且成为海量bug的源头。因此校验是一种典型的容易被轻视单又蕴含巨大工作量的事情,需要特别对待,专门设计。

一般来说,这根据校验根据其复杂度可以分为以下两类:

  1. 格式校验 格式校验一般是校验用户输入的格式是否满足要求,比如是否数字、电话号码、邮箱等等。此类校验的特点是校验域单一,一般只对一个input或者某个组件的value进行检查。格式类校验应与与用户展现非常接近,一种非常好的做法是将此类校验信息直接描述在html标签属性中。html5中input的pattern属性就是一种基于这种思想的解决方案。
  2. 逻辑校验 逻辑校验是满足格式校验后,继续进行的与业务相关的校验,例如是否存在相同用户名,输入的生日是否和身份证号不符等等。此类校验的一般涉及多个输入域,要综合处用户的输入内容一起校验。此类校验逻辑复杂,不适合写在html中。

目前有很多流行的form校验框架解决校验问题,如何引入合适的校验框架,先从理解校验这件事的过程开始。 典型的一个校验过程如下,用户在某个input处完成输入,应用在某个时刻被触发校验,可以是失去焦点或者keyup或者其他。被触发的校验过程找到此处input所需要的校验规则(有时候这个规则被直接写在html中)判单正确与否,如果正确,可能有提示,如果错误,可能也有提示。 从以上场景的描述中,可以找到校验的几个关键环节。这里局部采用一下管理学上经典的5w1h问题分析方法来分析问题

  • who: 哪个输入控件的内容需要校验。这是框架是解决不了的。要对哪个输入域做校验应该是应用传递进入的。
  • when: 何时被触发校验。比如说是“who”失去焦点时。变化太多,框架解决不了。只能被动触发。
  • what: 做什么校验。有时候这个”what”被写在html中。基本上,所有格式校验都是固定的,这个问题应框架解决。但框架应预留接口做更加复杂的业务校验。
  • how: 校验完毕后的动作。框架不能决定做什么,但是在校验结果出来后,框架应能知会到外部调用者。

在设计框架或者选择已有框架时,首先要区分框架的边界,简单来说,就是做什么和不做什么。框架应实现相对固定的业务流程。同时对可变部分预留足够的灵活性。

一个通用的校验框架一定是不含界面部分的。界面是多变和难以穷举的,是用tip显示错误,还是在输入域附近显示,是否需要动画,是否需要修改输入域的视觉状态,这些可变化的部分应为框架外部内容,由更专业的tip组件或者popup来完成。框架只应该负责在校验完成时候知会相关组件完成显示错误提示等若干事情。

基于以上的分析,校验框架应该具备以下规格 1. 解决what问题。内置了各种格式校验规则,如电话号码、e-mail等.并且能够灵活定义新的逻辑校验。 2. 解决who问题。说明如何根据输入的字符真正找到who对应的value。并且能够对于这个who使用哪些校验规则 3. 解决when问题。提供一个触发校验的方法。 4. 解决how问题。产生校验结果后能够知会外部的功能框架。

在淘宝机票订单应用中,依据上述原则自行设计了一个Validator框架,接口定义如下,Validator是校验框架对象。

  1. 在构造函数中提供表格化的校验逻辑定义型式。如下图所示,传递如下结构,定义每个字段对应的校验方式。在下图中,定义每行为一个field,每个field有若干rule,每个rule可以是框架内置的格式校验,也可以是自定义的逻辑校验,实际上是函数名。
  2. Validator框架提供validate()方法,validate方法有两个行为,如果不指定参数,将依次执行完所有field的校验,并且将最终结果返回。如果执行一个field name,框架将只校验field name对应的输入域。
  3. 一旦执行validate()方法,无论校验结果如何,框架均向其观察者发送事件’onValidate’。以便触发后续动作。
  4. 一些辅助参数,需要提供一个从field name找到输入域value的function。

总结

在处理复杂表单时,首先通过合理模块、组件划分,将复杂度分散。然后利用详细和广播机制解决分散的模块和组件间通问题。接着,过于复杂的组件要考虑进一步拆分,具体拆分的方式有纵切和横切两种,根据具体使用场景决定。最后,不要小看了校验,需要特别对待,专门设计。

相关 [复杂 应用 淘宝] 推荐:

复杂表单应用解耦,淘宝机票订单实践

- - 盒子UI
在web应用中,复杂表单这类web应用富交互元素多,业务逻辑复杂,犬牙交错,且需求变化频繁. 及容易成为晦涩和幽暗之地,也经常是各种代码坏味道的来源. 针对这种典型的复杂应用,本文以淘宝机票订单为例提出一种架构模式梳理和消化表单带来的复杂性. 解决复杂表单的的第一步,划分模块. 概念上,为了复用和解耦方便,应将模块按照功能的内聚程度进行划分.

"Java在淘宝AD系统中的应用"分享汇总

- - ITeye博客
条幅AD 搜索AD 定向AD. 高性能Web服务器 关系数据库,内存KV数据库, NoSQL (BigTable) 搜索引擎 排序算法(Ranking) 实时计算 海量日志收集和处理(MapReduce, 报表和结算) RPC,消息中间件 客户端技术:浏览器,Javascript. 多个子系统都采用Java实现:.

淘宝网前台应用性能优化实践

- - 淘宝中间件团队博客
本文曾发表于2013年4月的《程序员》杂志. 近年来,随着用户数和PV的增加,淘宝网的后端服务器数量增长很快;并且我们知道,Web页面延迟时间和转化率之间有着直接的关联. 出于提升系统吞吐量、降低成本、减少页面延迟、提升用户浏览体验、提高交易转化率的考虑,淘宝网在性能优化领域做了很多尝试. 本文将从应用性能分析、基础设施优化、应用自身优化、前端性能优化这四个方面,对淘宝网的优化尝试做一个总结.

HBase在淘宝的应用和优化小结

- - NoSQLFan
本文来自于NoSQLFan联合作者@ koven2049,他在淘宝从事Hadoop及HBase相关的应用和 优化. 对Hadoop、HBase都有深入的了解,本文就是其在工作中对HBase的应用优化小结,分享给大家. 原文地址: http://walkoven.com/?p=57. 文章PDF下载: http://walkoven.com/hbase:optimization and apply summary in taobao.pdf.

从淘宝网首页看内容随机展示应用

- - 互联网的那点事...
开始之前先解释下相关概念,当然都是按自己的理解杜撰的. 从下图可以看到,现在的网站,特别是电子商务网站,都有内容切换模块. 基本上分为两种,一种是tab内容切换/js轮转切换,都是用户可以手动点击切换或自动切换,用户可以查看所有内容,但一次只能查看一个单元内容,称之为“显性隐藏”;一种是没有切换按钮,后台有一个一定容量的内容池子,在当前模块区域内只能显示固定数量的内容,除非用户刷新网页,用户看不到其余的内容,称之为“隐形隐藏”.

小米工程师回应Android应用为啥启动慢|淘宝躺枪

- - 博客园_新闻
关于安卓系统体验差、不流畅的言论一直存在,尤其是应用启动速度相比 iOS 有着不小的差距. 对此,前锤子科技 CTO 钱晨@工头 Jeff 今天在微博上发问,启动 app 动作迟缓(卡顿)的本因是什么. 这一问题引发了业内不少人士的讨论,其中小米 MIUI 开发的@Mkey_小米表示,造成卡顿的原因有很多种,具体来讲, 包括应用启动时 CPU 给的不到位;内存不到位,甚至必须杀后台才能把当前的启动起来,还不行就走更慢的回收过程;还有一部分原因是应用写的差,比如淘宝,一启动五六十个线程,好像食堂开饭一样饥渴.

淘宝“伤”城

- 品味视界 - FT中文网_英国《金融时报》(Financial Times)
秦苏为英国《金融时报》中文网撰稿. 中国互联网的野蛮生长,再次震惊了电子商务市场. 10月11日晚间,为抗议淘宝商城大幅提高技术服务年费和保证金,约7000家中小卖家通过YY网络语音等组织方式,对韩都衣舍、欧莎、七格格、优衣库等大卖家进行攻击,包括利用规则进行购物、给差评、到货付款或申请退款等. 通过集中拍下某商品,导致这些商家的大部分商品下架“被拍死”.

淘宝维权记

- loudly - 马日拉
我先是在八月上旬从淘宝卖家“偶遇燕燕”处购买了两张高凳. 货送到时仅用塑料气泡纸包装,无硬纸包装. 因为外包装并无破损,所以当时就签收了. 快递走后,拆开包装,发现其中一个凳子凳面完全裂成两半,一个凳脚连接处完全碎裂. 两张凳子平放在一起,高度有一公分以上的落差,说明:1.卖家为减小货物体积,省略了硬质外包装,虽然物品表面由于有气泡纸保护完全无损,但运送途中,一个外形不规则的货物用脚指头想想也知道会因为堆叠、搬运等等情况,造成结构性的损毁.

淘宝养活谁

- - 《商业价值》杂志
成长在互联网时代的我们恐怕无人不知淘宝了,淘宝网由于其巨大的用户群成为了中国电子商务领域的航母. 然而这艘航母在行进过程中,伴生了无数相关联行业的成长. 首先获利的当然是淘宝的600多万商家,凭借着成本优势,电子商务已经在很大程度上深入人心,过亿的日成交额让不少淘宝商家获利颇丰. 数据显示,2011年TMALL平台品牌电商进一步增加,品牌数达7万多个,相较上年的3万个,品牌数增幅超过1倍.