本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Workflow-kotlin是一个Kotlin库, 用于创建可组合状态机, 及由状态机驱动的UI.
概览
Workflow-kotlin是一个应用框架, 它提供了架构上的基本数据类型.
Workflow-kotlin是:
- 用Kotlin而写并用于Kotlin
- 单向数据流库, 在每一个Workflow内部使用不可变数据. 数据从源向UI单向流动, 而事件从UI向业务逻辑单向流动.
- 支持以状态机的方式写业务逻辑和复杂UI导航逻辑, 由此实现了对于状态和正确性验证的自信推理.
- 对于特性和界面的可组合性和可扩展性的优化.
- UI框架将渲染"视图"数据类(包括事件回调)绑定到Android移动UI框架.
- 测试框架, 便于给应用业务逻辑编写单元测试, 并且保证了正确性.
在项目中使用Workflows
Kotlin
请查看square/workflow-kotlin仓库.
为什么是Workflow?
你想让我将必须要开发的应用特性拆开成分离的部件吗? 然后为每一个部件枚举全部可能的状态? 以及编写表示每个状态的类或结构, 以及每个组件可能传递给另一个组件的对象集合? 这听起来简直杀鸡焉用牛刀! 简单的事件干嘛搞得这么复杂? 为什么我要用Workflow?
我想那些总是在用Workflow的我们也会问这个问题 . 这是一个非常合理的问题, 我们想要在这回答. 回到问题的本质, Workflow有2个互补的正当理由, 我们如下展开:
- 软件的明析性, 正确性, 和可测试性(尤其是大规模的软件).
- 鼓励编程范式, 尤其是移动领域内最佳实践.
软件的明析性, 正确性, 和可测试性(尤其是大规模的软件)
我偏向于认为我们中的大多数已经遇到过: 这是我们连续第二天盯着200多名用户的日志. 我们知道问题是什么: 用户到了页面Y而对象foo的状态state却是bar, 但是foo在页面Y的时候状态不应该是bar.
foo为什么是bar?
不幸的是在进入页面Y的时候我们并没有foo的状态的调试日志. 我们只有用户尝试点击按钮Z时候的日志, 而且此时状态已经是bar了, 即使它应该是baz或buzz.
发生了什么? foo是如何在页面Y上变成状态bar的? 看了下代码, foo还在15个其它页面共享了状态, 并且在全部页面foo都是可变的. 页面Y中更新foo状态的代码, 耦合于按钮Z的交互, 所以我们不能简单地添加个单元测试, 我们需要复杂的UI测试来重新生成页面Y. 我们不知道这个问题是如何发生的, 这几乎意味着在没有付出巨大努力的前提下我们不能知道是如何发生的!
上面的故事有些戏剧性, 但是我希望它涉及的感觉是相似的. 在任何一个特性区域内, 从应用代码中推理并为所有可能的负面效应构建充分的精神模型, 是一件令人生畏的任务.
现在按比例增加一点数字 — foo被150个页面共享 — 那么这项一度艰巨的任务似乎几乎不可能完成.
所有的移动开发者面对过某种形式的上述问题, 在Square的Point of Sale应用中, 我们每天都要面对扩大版.
我们想要什么?
- 在每一个特性的软件部件之间有清晰的边界, 部件可以插入日志, 拥有可以测试的合同协议.
- 在特写特性的软件部件之内有清晰的输出期待, 部件能够通过测试验证正确性.
- 任何特定作用域内是不可变状态(比如. 在上述上下文中的页面Y), 这样, 在某些事件发生后提供新状态的代码处理可变性就位于一个可以检测和测试的"受保护区域".
- 来自UI表示层的状态更新有清晰的分离.
之所以要求上述条件是因为我们想要:
- 没有像开始的时候讨论的那样的bug. 换句话说就是, 我们要测试给予我们在应用逻辑上的自信.
- 在不可避免有bug的情况下, 我们想能够隔离场景, 再现精确的条件, 修复bug并且写下测试, 达到bug后续不再发生的目的.
Workflow通过提供了一种模型(和支持的应用运行时), 在原生移动应用上便利了这些目标, 这种模型相似于React, Elm或者任意数量的其它web应用JavaScript框架 (并非为了谈到即将到来的原生移动框架, 比如Jetpack Compose).
每一个逻辑部件分离进了一个Workflow, 这个Workflow拥有有限的状态集合和状态之间转换的逻辑. 不同的Workflow组合在一起成为完整的特性, 同时Workflow的签名指定了清晰的协议. Workflow运行时的事件循环为每一个Workflow处理了新的不可变状态的产生, 所以在Workflow渲染逻辑内部,状态是不可变的. 可以用额外的钩子以可测试的方式执行和检测Workflow, 以便对单元测试中的结果进行简单验证.
在更简单的层面上, Workflow通过为大型开发人员团队提供一个共享的软件组件习惯用法, 来讨论跨功能领域和跨移动平台(Android、iOS)的业务逻辑, 从而提高了清晰度. 此外, 由于应用程序由多个Workflow组成, 该框架支持功能之间的松散耦合, 以聚焦于代码更改的影响.