第1章 入 门 例 子
落笔之初,我需要快速地解释一下本书的内容,就是解释什么是领域特定语言(Domain– Specific Language,DSL)。为达此目的,我一般都会先展示一个具体的例子,随后再给出抽象的定义。因此,我会从一个例子开始,展示DSL可以采用的不同形式。在第2章里,我会试着把这个定义概括为一些更广泛适用的东西。
1.1 哥特式建筑安全系统
在我的童年记忆里,电视上播放的那些低劣的冒险电影是模糊却持久的。通常,这些电影的场景会安排某个古旧的城堡、密室或走廊在其中起着重要的作用。为了找到它们,英雄们需要拉动楼顶的蜡烛托架,然后,轻拍两次墙壁。
想象有这样一家公司,他们决定根据这个想法构建一套安全系统。他们进入之后,设置某种无线网络,安装一些小的设备。如果发生某些有趣的事情,这些设备会发出一条四字符的消息。比如,打开抽屉,抽屉上附着的感应器就会发出一条消息:D2OP。还有一些小的控制设备,响应这样的四字符命令消息,比如,某个设备收到D1UL消息,就会打 开一扇门。
所有这一切的核心是一些控制器软件,它们会监听事件消息,弄清楚要做什么,然后发送命令消息。在那个网络产业崩溃的年代,这家公司买到了一堆廉价的烤面包机,它们可以用Java控制,所以,公司准备用它们来做控制器。因此,只要客户买了哥特式建筑安全系统,公司就会进驻其中,给这个建筑物装上一大堆设备,当然,还有一个烤面包机,里面装有Java编写的控制程序。
就这个例子而言,我们的关注点在于这个控制程序。每个客户都有各自的需求,但是,只要有一些好的样本,我们就不难发现其中的通用模式。为了打开密室,格兰特小姐要关上卧室房门,打开抽屉,然后,再开一盏灯。而肖瓦小姐则先要打开水龙头,再打开正确的灯,从而开启两个密室中的一个。至于史密斯小姐,她的办公室里有一个上锁的壁橱,内有一个密室。她必须先关上门,把墙上的画摘下来,再打开桌子上的灯三次,最后,打开那个满满的橱柜最上面的抽屉,这时,壁橱打开了。如果在打开里面的密室前,她忘记了关闭桌子上的灯,就会警报大作。
这是个异想天开的例子,但它所要表达的意图一点都不特别。我们有这样一系列系统,它们共享着大多数组件和行为,却彼此间差异极大。在这个例子里,对所有的客户来说,控制器发送和接收消息的方式是相同的,但是产生的事件序列和发送的命令却不尽相同。我们要整理一下这些东西,这样,当公司安装一个全新系统时,付出的代价才会是最小的。因此,对他们而言,为控制器编写动作序列必须非常简单才行。
了解了所有这些情况,一种好的处理方式浮出水面:把控制器看做状态机(state machine)。每个感应器都可以发送事件(event),改变控制器的状态(state)。当控制器进入某种状态时,它就会在网络上发出一条命令消息。
至此,我应该坦白,写作之初,这是全然不同的。状态机是一个很好的DSL例子,因此,我先选择了它。之所以选择 哥特式城堡,是因为我厌倦了其他所有状态机的例子。
格兰特小姐的控制器
这家神秘的公司拥有成千上万感到满意的客户,但在这里,我们只准备强调其中的一位:格兰特小姐,我最喜欢的客户。她的卧室里有个密室,通常情况下,这个密室都会紧锁着,隐蔽在那里。要打开这个密室,她就要关上门,然后,打开柜子里的第二个抽屉,打开床边的灯─二者顺序任意。做完这些,秘密面板就会解锁,她就可以打开密室了。
我用一张状态图来表示这个序列(见图1-1)。
如果你没接触过状态机,其实,它们只不过是描述行为的一种常见方式而已─并非广泛适用,但对于类似这样的情况,却是再合适不过了。其基本的想法是,控制器会处于不同的状态。当它们处于某个特定的状态时,某个事件会让控制器转换为另一个状态,在那种状态下会有不同的转换(transition)。因此,一系列的事件会让控制器从一个状态进到另一个状态。在这个模型里,当进入一个状态时,会执行某个动作(比如发送消息)。(其他类型的状态机可能会在不同的地方执行动作)。
基本上,这个控制器就是一个简单的传统状态机,不过需要一些微调。客户的控制器要有一个明确的空闲(idle) 态,系统会有大多数的时间处在该状态。某种特定的事件就可以让系统跳回到这个空闲态,即便它正处于一个更有趣的状态转换中间,这样就可以有效地重置整个模型了。在格兰特小姐的这个例子里,开门就是这样一个重置事件。
引入重置事件,意味着这里描述的状态机并不完全满足某种经典的状态机模型。状态机有几种非常有名的变体,这个模型便是以其中一个为起点的,只是略做微调,增加了重置事件,也就变成了只针对这种情况。
需要特别注意的是,严格来说,未必一定要有重置事件才能表示格兰特小姐的控制器。一种替代方案是,为每个状态添加一个转换,只要触发doorOpened,就会转换为空闲态。重置事件这个想法很有用,因为它简化了整个状态图。