github.com/rainforeste…
github.com/rainforeste…
这是一种声明式的通用编程。看起来它很像 Excel 里的公式计算,很适合解决复杂计算问题,除此之外,在解决业务逻辑问题上,更能大显身手。
从命令式写法到这种声明式写法就像“从算术方法到方程式方法”。所以,其自然具备了方程式方法的优势,同时也不可避免地沾染了某些劣势:冗长繁琐、不易理解?“不易理解”需要打个问号,毕竟在习惯方程式思维之后,这会变为优势,在数学上如此,在编程上也是如此。
优势:解决了什么问题?
1. 降低开发复杂度
- 直接映射业务逻辑:可以将业务逻辑直接映射为代码,不需要推导业务需求,就像在数学上不需要推导算术过程一样,方程式方法更具有通用性,任何需求都适用于通用的求解方法。
- 示例:业务需求是获得 A,而 A 是 X 与 Y 的和。我们可以直接写出如上示例中的计算规则,无需推导和构建正确的计算步骤,不需要。
2. 提升稳定性和可维护性
-
适应需求多变:需求变更时只需增删或调整规则定义,不需要重构执行过程,就像在数学上不需要重构算术过程一样。
-
无需了解全局:传统思维会认为,在没有完全弄懂程序的所有细节之前是不可能正确修改代码的,因为每一行代码都堆叠在前面的代码之上,移动一点儿都可能崩塌。 但这种方式不会。你完全不必了解全部规则,也能放心大胆地修改代码,不会出错。为什么呢?举例说明,假设需求变了,要把程序改为
A = X + Z。你有两个选择:- 方法一(微调):找到
A = X + Y的地方把 Y 替换为 Z; - 方法二(覆盖):忘掉之前的
A = X + Y,管那么多干啥,直接新写一个规则A = X + Z把之前所有关于 A 的覆盖掉就好了。
- 方法一(微调):找到
-
维护难度恒定:从方法二可以看出,即使程序维护了上百次,你也可以完全忽略已有规则,就当它们都不存在,把所有变量都当作是“一手”的,大不了就把新需求从头写一下而已。
- 若按照命令式写法,你首先得在计算 A 之前,先计算出新的 Z ,而 Z 有可能是异步的,所以不得不打乱原有的同步计算 A 的过程,重构代码。而这种方式则不会,修改局部即只作用于局部。程序维护一次和维护一百次时维护难度是相同的。
其他
写法繁琐
确实,声明式在简单场景下可能显得冗长。对于简易项目或脚本,推荐使用命令式写法;稍上规模的或需要长期维护的项目,声明式写法带来的稳定性和可维护性,足以抵消其初期的繁琐。
难以阅读、不易理解?
正如从算术思维转变为方程式思维一样,需要经历一个适应的过程,在熟悉之后你将会改变这一看法,它其实更易阅读、更易理解。你可以在不了解全局的情况下,准确无误地阅读和理解任何一组规则,当然也能正确修改。
能写某某算法吗?
能,但不适合,算法是纯计算过程的函数即纯函数,直接按顺序计算,用命令式写法更合适。既然不适合,那不是与前文“很适合解决复杂计算问题”矛盾吗?它做的是和 Excel 相同的事,Excel 适合写算法吗?会有人用 Excel 写算法吗?
Debug 难度
很多人会错觉高估 Debug 难度,总觉得由于它内部依赖复杂可能会很难 Debug。
- 采用此范式写的代码,正确性通常很高,极少需要 Debug;
- 当错误发生时,由于规则严谨自洽,错误一般不会传播很远,可以很快被定位到,通常下一个就定位到了。
如何写测试用例
这非常简单,比以往简单得多,因为它是一个数据结构,你赋值了某个属性,然后去断言目标数据是否符合预期即可,仅此而已。你可以轻松地测试一个规则、一组规则甚至全部。
静态分析
这具备天然优势。可以实现一个静态分析的辅助工具,自动生成整个程序的可视化依赖图。这张图即代表了程序的业务逻辑,这在命令式写法中是几乎不可能的。
递归、循环
当遇到递归、循环时,程序会第一时间指出位置,防止因失误不小心引入潜在问题。
必须使用递归、循环时,文档中也提供了解决方案。
付费
允许免费使用,是否付费全凭自愿。