三思系列:"声明式UI"和"命令式UI",你的理解可能是错的
三思系列是我最新的学习、总结形式,着重于:问题分析、技术积累、视野拓展,关于三思系列
前言
最近,Jetpack Compose
发布了 Beta
版本,可以说是千呼万唤始出来。
在一个 现象级事物
出现时,关于它的讨论会很热。我注意到,最近有一大批关于Compose的文章涌现了出来,并且有很多的讨论,这很棒。
但是,值得一提的是,我发现很多人对于 声明式UI
、 命令式UI
的认知,可能出现了一点偏差。
当然,我也 不是
像 马丁老爷子 或者 Jack Wharton 那样的 专家
,对于这个问题,只能是同大家 一道探索
。
声明式 和 命令式 含义探索
按照经验,中文往往比英文具有 更强的表达力
,这也意味着:中文的 凝练度更高
,也更容易产生理解误差,我们结合英文一起看
- 声明式 declarative
a declarative sentence has the form of a statement.
statement: 4条释义,看第一条
- something you say or write, especially publicly or officially, to let people know your intentions or opinions, or to record facts
- 其他三条略
- 命令式 imperative
technical, an imperative verb is one that expresses an order, such as ‘stand up’
如果从 语境
上来看,声明
是面向一个环境、一个群体表达某些规则,而 命令
所面向的,是一个明确的对象群,在指使他们做一些事情。
举个可能不太恰当的例子:
我军优待俘虏,缴枪投降不杀,这是对敌军的声明
营长命令各战斗单位,打扫战场,一连收缴武器弹药,二连负责收押俘虏。这是命令式
我发现,讨论 声明式
和 命令式
时,需要先界定好 讨论范围
。这一点非常重要,一旦超越了范围,讨论就会出现错误。
放在计算机领域中讨论这两者时,需要先界定好 层
。
我们知道,程序设计中,以及在计算机设计中,都是 分层
的。下层
的内容交给 上层
使用时,需要使用到 接口
,
这个接口,不仅仅是我们编程中的API的概念
甚至,接口的表现形式是一种 语言
。
接口被定义时,其表意即已固定,当其表意:
- 越倾向于
现实表达
时,呈现为声明式
- 越倾向于
执行过程
时,呈现为命令式
UI
User Interface 的简称,直译为用户界面
UI系统需要处理两个范畴的事情:
- 内容呈现、反馈
- 用户交互
另外,还需要程序 构建
出UI。
我们需要从这 三个角度
来看。
构建UI时的 命令式 和 声明式
我们在Android原有的知识领域中看这个问题。
我们知道,这部分内容设计时是遵守 OOP 的,存在一整套 View 类簇
。而界面由具体的功能类组建出 视图树
。
我们需要讨论的,就是 视图树
的 构建
。我们知道:
ViewGroup
存在一系列addView
方法,通过它们,最终实现树结构
这套 接口
,倾向于 执行过程
,直接使用这套接口去构建,就是 命令式
的构建。
而在此基础上,封装了 LayoutInflater
,将 xml语法
表达的 结构树
转换为 视图树
, 此时我们使用的 接口
,其表现形式是 xml语言
。
按照特定的规则,我们 直接描述
了 期望的结果
,这更倾向于 现实表达
,这样的构建方式,就是 声明式
View
存在一系列的布局属性
,一些直接持有,一些被LayoutParams封装而间接持有。
在 视图树
的节点构建过程中,还需要处理 布局属性
。
按照刚才的讨论,我们很容易得出:
- 直接使用
View的API
或者LayoutParams的API
,或者直接对属性赋值,可以断言是命令式
范畴。 - 而在布局文件中,声明对应的属性值,可以断言是
声明式
范畴
小结:本段内容,我们讨论了构建UI的两种做法。并讨论了其所属的范畴。
Android体系最基础的内容中,内容呈现&交互
在最基础的内容中,讨论 内容呈现
和 交互
部分使用的API,是 命令式
的,还是 声明式
的,这 非常的无聊
。
内容呈现时的 命令式 与 声明式
在 View
提供的 接口
中,例如:
- TextView#setText(Charsequence cs)
- ImageView#setImageDrawable(Drawable d)
等,更加偏向于 执行过程
。
但是注意,此时 执行过程
和 现实表达
的界限已经有点模糊了 。我们不必 牵强附会
。
读一下这两句话:
- 我们通过调用相应API设置了需要显示的内容
- 我们在布局文件中指明了视图显示时需要呈现的内容
交互时的 命令式 与 声明式
我们知道,Android View 体系中,封装了一系列 Listener
,通过 接口回调
,让程序可以 监听
到用户发出的 交互指令
。
除此之外,在 最基础内容
中,仅有简陋的 click
属性可以声明点击事件的 消费函数
此时,我们 放大着眼点
。我们处理视觉呈现和交互时,面向的两个重要对象
信息展示
| ================ |
| 内容呈现 \|/
|=====| |=====|
| 实体 | | UI |
|=====| |=====|
/|\ 交互 |
| ================ |
状态变更
这两者之间存在两条线:
代表信息
的实体
->UI
, 这是内容呈现
,实质是:在UI上做信息展示
UI
->代表信息
的实体
,这是交互
,实质是:在信息实体上做状态变更
,即变更信息
我们在实现这两条线时:
- 如果
直接利用
了倾向于现实表达
的高阶框架封装,那这个框架就是声明式
的框架; - 如果仅使用
低阶
的view层api
,自行实现了逻辑,那就是命令式
的编程,毕竟也没用啥框架;
评价一套
UI工具体系
是否是声明式
的,要从这三个方面看,如果在三个方面均进行了封装,提供的接口
都偏向于现实表达
,那是成熟完善的声明式UI
框架。
看几个例子:
- 直接使用
View体系的接口
构建UI、处理显示和交互,这是命令式编程
- 使用
LayoutInflater
工具和xml表达的布局文件
。绝大多数情况下,还需要通过映射关系
,找到View对象;并进一步根据业务逻辑
,操作View对象
实现内容呈现和交互逻辑。 这是声明式构建
,命令式编程
处理显示和交互。这套工具并不是完善的声明式UI开发工具
- 使用
LayoutInflater
工具 +DataBinding工具包
和xml表达的布局文件
。DataBingding
+LayoutInflater
是一套完善的声明式UI框架
Compose
,完善的声明式UI框架
思考:为什么当初XML布局方式受到官方推荐
很大一方面是Java语言特性的原因,最开始,都是使用Java语言编写业务逻辑,而基于Java,很难直接定义DSL
注:
Java语言很难直接定义DSL
这一点我并没有做严格的调研考证,如果有谬误,请指出。而基于Groovy等语言开发符合
DSL
的声明式UI框架
,必然要引入Groovy,这在当时也和主流
格格不入,虽然Google很喜欢创新。
所以,官方提出了 布局
和 业务
分开,这样也有很多好处:
- 业务逻辑的
模块内聚性
和复用度
可提升 - 开发者可以提升专注度
xml树
和view树
之间关系明了
,借助成熟内容,快速开发出预览工具
思考:为什么XML布局方式走在被淘汰的路上
因为使用XML布局的方式存在 先天弱点
,它和 业务逻辑
天生生活在两个世界。
这意味着,必然存在一个 加载器
或者 转换器
,在 运行时
或者 编译时
,将 xml语法
所描述的 声明式布局
转换到 业务逻辑
所在的世界。
所以,并不是 DataBinding
这一 声明式UI框架
不够优秀,而是 xml 和 业务逻辑代码 之间天生的屏障不够 人道主义
。
而推行 kotlin first
也有一段时间了,基于kotlin,开发 DSL
工具包非常的方便。
延伸:响应式UI框架
这也是 Modern Development 中非常热门的一个词,响应式
是对特点的一种描述,方法论是:事件驱动,Event-Driven
,发布订阅者模式
。
这个概念,和前面提到的 声明式UI框架
和 命令式UI编程
没有必然的联系,它所表达的是: 代表信息的实体
和 UI
之间的互操作是符合 响应式
特征的。
但可以想象,要实现响应式,其接口变现特征,天然倾向于 现实表达
;如果是倾向于 操作过程
,可以断言其抽象程度非常低。
扣题:勘误
再看到这些说法,我们可以判断出它们存在 谬误
:
- 基于xml无法实现声明式UI
- xml被淘汰是因为无法实现声明式UI
- 基于xml布局方式都是命令式
- 声明式一定会取代命令式,你看xml都要被淘汰了
- 用代码写的就是命令式UI
等。
再多聊两句,一套曾经热门的技术,一定有其热门的原因和背景,它走向消亡,往往是其 面向的问题
和 场景
,在事物发展过程中,越来越少见。
声明式
和 命令式
各有优劣。按照规律,高阶封装更 人道主义
,解决高复杂度问题时,也容易实现 降维打击
。
但这也意味着机器付出的代价更高一点。
总之,脱离了环境背景讨论谁胜谁负,往往是耍流氓。
总结
这一篇,我们用比较 随性
的文字,对 声明式UI
进行了一次脑暴,进而讨论其概念的实质。我相信,经过这次脑暴,对这一套概念体系的理解会更加 深刻
、
准确
。相应的,学习新内容时也会 更加轻松
、事半功倍
进一步延伸, 在学习Compose,思考Compose的优劣时,势必会上升到
OOP
和FP
两大编程范式的讨论。这一篇不再展开。点赞关注收藏,从此不迷路。