文章目录
  1. 1. 决策表
  2. 2. 产生式规则系统
  3. 3. 状态机
  4. 4. 依赖网络
  5. 5. 选择模型

当谈到DSL的好处时,人们经常会说“DSL支持声明式编程”。

主流编程语言大都遵循命令式计算模型。这更方便理解程序的实现—如何实现程序的目标。但是在理解程序意图方面,命令式模型就未必合适,需要考虑一下其他计算模型啦。

先看一个简单例子:























has cell phone Y Y N N
has red car Y N Y N
points 7 3 2 0

命令式模型的C#代码:

public static int Calcpoints(Application a) {
    if(a.HasCellPhone && a.HasRedCar) return 7;
    if(a.HasCellPhone && !a.HasRedCar) return 3;
    if(!a.HasCellPhone && a.HasRedCar) return 2;
    if(!a.HasCellPhone && !a.HasRedCar) return 0;
    throw new ArgumentException("unreachable");
}

使用布尔条件的风格相对简洁一些:

public static int Calcpoints(Application a) {
    if(a.HasCellPhone)
        return (a.HasRedCar) ? 7 : 3;
    else  return (a.HasRedCar) ? 2 : 0;
}

尽管表格与第一种风格的代码之间有些相似,但毕竟不一样,命令式模型强迫if语句按顺序执行,但这并不是决策表的本意。

命令式的展现方式有一个潜在的严重缺陷,它会丢掉一些有用的机会。决策表就有这样一个好处,可以检查它,确保没有遗漏或者重复某些排列项。

使用命令式代码还有一种实现方式,创建决策表抽象,然后根绝这里的特定情况进行配置。如下:

var table = new DecisionTable<Application, int>();
table.AddCondition((application) => application.HasCellPhone);
table.AddCondition((application) => application.HasRedCar);
table.AddColumn(true, true, 7);
table.AddColumn(true, false, 3);
table.AddColumn(false, true, 2);
table.AddColumn(false, false, 0);

现在这个决策表的展现更加忠于原貌:不在命令式代码中指定条件求值的顺序,这部分逻辑留在决策表内部。更重要的是,决策表对象本身可以检查条件的正确性,然后告诉我是否遗漏了某些条件。还有一个额外奖励:执行上下文从编译器转移到运行期,这样就可以修改其中的规则而无需重新编译。

Martin把这种风格称为“适应性模型”。其特征在于:行为很大程度上是由模型的实例(以及实例的组合方式)决定的。如果不看对象实例的配置,是无法预期会有什么行为。

使用适应性模型不需要DSL。很多时候,适应性模型是一个很好的语义模型。

使用适应性模型的一大弊端是:行为的定义是隐式的,我们透过代码看出背后实现了什么。也就是说,尽管意图通常更容易理解,但实现却更难理解。调试是一个很大的问题。

能够使用其他计算模型,是采用DSL的一个重要原因,所以下面就开始介绍一些比较常见的计算模型。

决策表

上面已经提过决策表,它所有的条件都是布尔条件;更复杂的决策表可能包含其他形式的条件,例如,数值范围。

对于非程序员来说,决策表特别容易理解,很适于和领域专家沟通。

产生式规则系统

是一种逻辑进行建模的常用概念:它将逻辑分解成多条规则,每条规则由条件和由此引发的行为两部分组成。如采用命令式代码,每条规则都可以用类似于if-then语句风格表述:

if 
    passenger.frequentFlier
then
    passenger.priorityHandling = true;

if
    mileage > 2500
then
    passenger.frequentFlier = true;

我们可以发现第二条规则可以触发第一条规则,这种性质称为级联(chaining),是产生式规则系统的一个重要性质。有了级联,我们就可以单独编写规则,无需顾虑它们在更大范围内造成的影响,让系统自己找到结果。

但是产生式规则依赖大量隐式的逻辑,经常会做出意想不到的事情。

和决策表相比:当使用产生式规则系统时,每次只关注一条规则的行为;而使用决策表时,则要同时关注整张表。

状态机

状态机把对象的行为划分成一组状态,用事件触发行为,感觉对象所处的状态,事件会使对象从当前状态转换到另一个状态。

状态机的核心元素包括状态、事件和转换。

依赖网络

软件开发者最熟悉的计算模型,各种构建根据(如Make、Ant)背后的基础模型。在这个模型中,我们关注需要执行的任务,搜寻每个任务的前置条件。

如果有一系列耗费大量计算资源并且彼此依赖的任务需要管理,依赖网络就是很好的选择。

选择模型

很难给出具体的指导原则,最好的方法是试一下:先在纸上尝试用简单的文字和图表来描述行为;如果某个模型似乎能通过这个简单的纸上测试,那就值得把它构建出来。关键在于得到一个运作良好的语义模型。我们倾向于首先投入讲多精力来优化模型,然后再追求易读的DSL。

文章目录
  1. 1. 决策表
  2. 2. 产生式规则系统
  3. 3. 状态机
  4. 4. 依赖网络
  5. 5. 选择模型