6. 片段

到目前为止,我们给出的例子虽然并不非常复杂,但某些例子的描述已经开始显的不太清晰。

而良好软件设计的核心原则之一就是 清晰性 ,对于其它编程语言而言,满足清晰性的一个重要措施就是允许用户定义更小的,职责更加单一的小函数, 或者小类。

而通过 Transaction DSL ,你同样可以将一个复杂过程的任何一个局部进行提取,以定义更小的,职责更加单一的子操作: 片段 (Fragment)。

6.1. __def

定义一个片段的方式,就是通过 __def 。比如:

__def(Fragment) __as
( __asyn(Action1)
, __asyn(Action2));

从例子中可以看出,一个片段的定义包含两个部分:首先,__def 的参数,定义了一个 片段名字 ; 然后在 __as 里,定义了这个片段所执行的操作。

之所以会有 __as ,主要是作为一门以 C++ 为母语言的 DSL,在 C++ 的语法范围内,尚未找到一种更好的实现方法。

6.2. __apply

一旦一个 片段 被定义,就可以在另外一段操作中引用它。比如:

__transaction
( __asyn(Action1)
, __apply(Fragment));

现在,我们就可以将 procedure 中的例子的两个片段提取出来:

__def(Fragment1) __as
( __asyn(Action1)
, __asyn(Action2))
, __finally(__rsp(Action3)));

__def(Fragment2) __as
( __asyn(Action5)
, __finally(__sync(Action6)));

最后,我们的事务描述就变成了下面的样子。和之前的例子对比一下,是否清晰了许多。

__transaction
( __apply(Fragment1)
, __asyn(Action4)
, __apply(Fragment2));

6.3. __params

对于软件设计而言,另外一个重要的指标是 可重用性 。之前定义的子操作,虽然最早的出发点是为了清晰性,但事实上,也间接的让这些片段变得可以复用。

但一旦到了复用的层次,我们就需要更加强大的设施。比如,存在如下两个事务:

__transaction
( __time_guard
    ( TIMER_1
    , __asyn(Action2)
    , __asyn(Action3))
, __asyn(Action4));

__transaction
( __concurrent
    ( __asyn(Action1)
    , __time_guard
        ( TIMER_2
        , __asyn(Action4)
        , __asyn(Action3))));

稍微仔细的观察,可以发现,两个事务有一段非常相似的过程:

__time_guard
( TIMER_1
, __asyn(Action2)
, __asyn(Action3));

__time_guard
( TIMER_2
, __asyn(Action4)
, __asyn(Action3));

两段子操作看起来很相似,但中间有两处细节是不同的。对于这类问题,我们可以将它们合二为一,然后将差异参数化:

__def(Seq2, __params(__timer_id(TIMER), __action(ACTION))) __as
( __time_guard
    ( TIMER
    , __asyn(ACTION)
    , __asyn(Action3)));

在这个例子中,我们定义了一个名为 Seq2 的片段, __params 里放置的是它的两个参数: TIMERACTION 。 前者的修饰 __timer_id 说明参数是一个 Timer ID , 后者则由 __action 修饰,说明它是一个操作。 这些修饰就相当于其它编程语言里的 参数类型说明。

这两个参数随后在 __as 里所定义的操作里得到了使用,其中 TIMER 被用在了 __time_guard 里, 而 ACTION 则用在了第一个 __asyn 里。

6.4. __with

调用一个带参数的片段,同样是使用 __apply ,不过,需要通过 __with 来指明实参。 现在,我们就可以将之前的两个事务修改为:

__transaction
( __apply(Seq2, __with(TIMER_1, Action2))
, __asyn(Action4));

__transaction
( __concurrent
    ( __asyn(Action1)
    , __apply(Seq2, __with(TIMER_2, Action4)));