什么是好的模型?

昨天在介绍模型驱动的时候,有位同学问我如何设计一个好的模型?这是一个很难回答的问题。因为好很难定义,如何到达所谓的好更没有一定之规。

首先,好的定义往往是场景相关的,一个当前最优的选择随着场景的变迁也可能不再是好的选择。即使是放之四海而皆准的更好,比如广义相对论 vs. 牛顿力学,在解决具体问题的时候,也可能简单的牛顿力学更实用。

第二,如果我们能够设计出一个适用范围很广、能够应对很多变化的模型,那么本质上的原因也是领域逻辑具有某种内在的规律,模型只是这种规律的一种自然体现。软件设计目前还远谈不上是一种精密、复杂的科学,即使不懂得任何设计理论,多观察一下别人的成功和失败经验,一般也能达到一个差不多的程度。

第三,创造性的解决问题的方案在一开始可能显得离经叛道,与当前的生态格格不入,反而有可能看上去是不好的。

虽然很难以一种明确的方式去定义什么是好的模型,我们仍然可以根据一些表观条件来过滤掉一些次优的选择。

  1. 设计是多目标优化的。我们可以把问题分解为多个维度,在每个维度上单独衡量,然后再综合评估。

  2. 设计是多层次的(空间),不要试图设计一个最好的、万能的模型。比如,存储层和应用层可以有不同的对象结构,没必要使用统一的对象结构一统到底。使用DDD领域模型驱动也不需要排斥根据物理模型来生成实体存储层的代码。不同抽象层次、面向不同使用意图的元素不要混杂在同一个模型中。基于可逆计算理论,可以通过增量化代码生成来实现不同结构的模型共享部分基础信息。

  3. 模型应是面向演化的(时间),不应过早引入限制未来选择的设计决定。基于可逆计算理论,可以通过差量机制永远为模型保留最细粒度的扩展能力和多模型融合的能力,可以通过元编程弥补组件和插件机制的不足。

  4. 模型的复杂度应适中。解决方案的复杂度与问题的复杂度,以及具体执行人所能适应的复杂度要相匹配。简单的问题不应该采用明显过于复杂的解决方案。如果执行人的能力范围有限,即使一个方案可能在技术层面更优,但更容易出现误操作、出现问题更难独立解决,那么可能还不如使用执行人能够充分理解、具有充分自主控制权的方案。反之,一个复杂的问题可能不存在简单的解决方案,简单粗暴将意味着拆东墙补西墙,疲于奔命。

  5. 模型需要明确定义它对外部环境的依赖,并尽量缩减它对外部的依赖点。模型的价值首先在于它可以被独立的解释和认知。

  6. 模型内在的概念完备性和一致性非常重要。基于可逆计算理论,对于模型可参与的运算,我们需要进行配对设计,即每个改变状态的动作(差量)都对应一个反向动作(差量),使得系统在某种意义上可以恢复到前一个状态。不能反向往往意味着信息记录的缺失和设计空间的不完备性,面对异常情况时会无法处理。

  7. 任何一个具有一定复杂度的模型都应具有分解、合并和二次抽象的机制。比如支持组件封装,import子模型等。可逆计算理论为此提供了一整套通用的解决方案,无需针对每种模型再进行单独设计和实现。

  8. 对模型信息的处理利用可以是多阶段的。应尽量在代码生成、编译期、预处理等阶段进行处理,不要把与运行时状态无关的逻辑和运行时逻辑纠缠在一起。

  9. 模型的内容重于形式,但是形式应允许反向析取。大量使用某种语言或者某种框架内置的机制来表达模型,虽然可以简化一次性编写的成本,但长期来说对于模型演化并不有利。为了最大化模型的价值,模型作为描述性信息应该是多用途的,不应只服务于某种单一目的,需要支持对模型信息进行再加工和形式转换。

  10. 模型应该由元模型来定义。通过元模型可以最大限度的发掘不同模型之间的语义和结构共性,便于实现它们的互联互通。通过元模型可以自动生成模型解析器、IDE插件和设计器等。