NopReport 为什么是一个非常独特的报表引擎?
NopReport 与一般的报表引擎不同,它可以直接采用 Excel 和 Word 作为模板,而不一定需要使用专用的可视化设计器。
NopReport 的介绍参见采用Excel作为设计器的开源中国式报表引擎:NopReport和如何用800行代码实现类似poi-tl的可视化Word模板
但是有些人看了介绍之后,感觉不到它的独特之处,提出如下疑问:
这种方法其实老早就有人会了吧?大家都知道 word 其实就是 xml,可以直接用模板引擎填充。整体有什么特别颠覆认知的东西吗?jxls 也是用 excel 的批注来写模板语法。
要体会到 NopReport 设计的创新之处,必须要跳出具体的功能细节层面,从更抽象的数学结构层面去思考。整个 Nop 平台的设计与现有其他开源框架最本质的区别在于,Nop 平台的设计都是基于第一性的数学原理推导得到,因此它的做法不是某种聪明的设计模式,而是体现了某种数学上的必然,是在某种数学的意义上可以严格证明的最优化的设计。
通用的模板可视化方案
NopReport 的核心设计思想是基于如下数学公式:
在数学的意义上,任何一个原始模型都可以看作是一个合法的生成模板,当我们需要更丰富的模板行为时,只需要在原始信息之外再补充扩展配置即可。比如说一个普通的 Word 文件就可以看作是一个 Word 报表模板,报表引擎可以读取普通的 Word 文件,然后把它转换为报表模板,只不过它的运行结果相当于是一个恒等变换:即无论我们输入什么样的报表参数,我们都是输出恒定的报表内容,这个内容也就是我们读取的 Word 文件的原始内容。
当我们需要得到一个包含判断、循环等复杂动态内容的报表模板时,很多人的第一想法是从头开始设计一种报表格式,它满足我们所需要的动态配置要求。但是按照前面的数学分析,既然普通的 Word 文件就是一个合法的报表模板,那么我们的报表模板的表示能力必然是原始模型的表达能力的一种超集,最简单的情况下,我们应该采用一种线性化的设计,即我们完全保留基础模型的设计要素,通过额外增加新的扩展模型信息来引入动态模板能力。注意,这里强调的是我们完全不修改此前的 BaseModel,扩展的 Model 相当于是一种独立存在的 Delta 差量化表达。
当我们需要进行可视化设计时,在数学的层面,相当于是引入一种双向可逆变换:
可视化设计器负责将模型信息展现为可视化的视图,并提供相应的编辑手段。反向的,针对可视化的视图,我们可以将其中的信息序列化,得到可以用文本(或者二进制)形式保存的模型信息。可视化视图和模型的文本表达是同一信息的两种不同表现形式,可以在它们之间建立双向转换关系。
一种理想的可视化映射应该满足线性映射原理,即满足如下数学公式:
当模型层面存在 Base+Ext 这种线性分解的时候,我们希望将这种关系保持下来,在可视化设计层面也得到 Base 设计器+扩展设计器这种线性分解关系。从数学的意义上说,我们希望可视化映射是一种数学上的同态映射。如果套用范畴论的术语,我们可以说这是一种函子(Functor)映射关系。
将上述的数学公式对应到具体的 Office 软件,我们的目标就是利用 Office 软件中某种内置的扩展机制来存放扩展信息,并自动提供针对扩展信息的可视化设计界面。如果一款软件是遵循可逆计算原理构建的,那么它必然会采用 (data,ext_data)的配对设计方案,在任何一个 DSL 语法节点处都允许引入扩展数据,通过扩展的 xdef 元模型就可以定义扩展数据的元语义,并根据 xdef 模型定义自动生成可视化编辑器。但是因为 Office 的设计没有按照可逆计算理论进行,这使得我们只能通过某种 Hack 的方式来重新诠释它的内置功能,将其中的一些改造为元数据扩展机制。NopReport 的具体做法就是利用 Word 中的超链接机制,将xpl:xxx
这种形式的超链接解释为扩展信息节点。
使用超链接只是一种不重要的技术细节。我们也可以选择使用 Word 中的批注机制来保存扩展信息。
需要强调的是,利用上面的方案,我们可以实现任何模型的模板化扩展,所需的只是补充少量 Delta 信息而已。关于以上线性映射原理的进一步分析,可以参见我的文章从张量积看低代码平台的设计
通用的结构层构造规律
很多人都可以直观的感受到,自从 Office 采用 OOXML 这种 XML 存储格式之后,对于 Office 格式的处理变得大为简化了。现在有很多 Office 模板库如 jxls 和 poi-tl,底层仍然是基于 Apache POI 技术来操作 Office 文档,它们在 POI 的基础上通过深度封装,提供更加简单直观的模板生成方案。但是 NopReport 通过极少的代码就可以取代这些模板库的功能,同时提供更好的可扩展性,这是为什么?
本质上这是因为 POI 库是在类型特化的对象层进行操作,而 NopReport 是在均一化的 XML 层面进行操作。XML 可以看作是一种通用的结构表达方式。可逆计算强调我们在将信息转化为业务对象之前,存在统一的结构表达层,可以直接在这个层面完成很多通用操作,没有必要把处理放到对象层。对象层每个对象的类型不同,造成的对应的处理规则也不同。正如千变万化的建筑作品背后是统一的工程力学,在结构层看来,很多业务层面不同的东西本质上是一样的,遵循同样的结构构造规律,可以采用同样的处理工具和手段。
在这一过程中,XLang 语言中的 XPL 模板子语言具有特殊的重要性。XPL 在数学上的定位是什么?它负责将任意的 AST 表达树通过语法制导翻译转换为可执行逻辑。这里所谓的语法制导翻译(Syntax Directed Translation)指的是我们在遇到某个语法节点的时候,自动触发对应的翻译规则。这实际上就是一种上下文无关文法的自定义组件机制。
每一个<orm:ForEachEntity>
这样的标签都会触发对应的组件定义,并执行组件内的可执行逻辑。
对于结构层构造规律的进一步分析,可以参见我的文章通用的Delta差量化机制
基于可逆计算理论设计的低代码平台 NopPlatform 已开源:
版权声明: 本文为 InfoQ 作者【canonical】的原创文章。
原文链接:【http://xie.infoq.cn/article/83348e8f05e89d71bb10f8cad】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论