例子不是影响他人的主要素材,是唯一素材。 ————原书引用。
这一篇主要以官方提供的哲学家就餐的例子,来引出顺序图、活动对象、到整体的设计与应用,一切从需求开始,一张圆形的桌子上围坐了五个哲学家,圆桌上有一盆意大利面,在哲学家的两侧都有餐叉,哲学家的日常由思考和吃面组成,思考一段时间,饿了,然后吃面一段时间,然后再思考,但是有个问题,就是面条太滑,如果想要吃面就得用两把叉子,这个时候出现了一个问题,当一个吃面的时候,身边的另外两个人就不能同时吃面了,因为餐叉有限,有人获得了,身边的人就只能等待,如图:
在这个例子中,我们很明显能够确认的是,餐叉相当于是一种资源相对于哲学家,基于上一篇,我们规则里面,一般情况下将这种共享的资源封装在一个活动对象内,统一管理,这里就有了第一个名为table的活动对象,负责管理餐叉,因为餐叉都是放在桌子上的,所以其名为table。除了这个共享的资源以外,看一看谁来申请餐叉,哲学家,同时,他还有不同的状态,思考,饥饿,用餐等等。所以很符合状态机的模式,于是将哲学家也封装成活动对象,整个应用便是通过活动对象之间的交互完成的。
接下来需要分离出整个应用当中需要的事件信号,这时候需要绘制顺序图,顺序图如下:
这个图很有意思,最左上是QF提供基础的服务项,往右都是我们定义的活动对象,
从每个活动对象垂直看下去,就是活动对象在交互过程中的状态转换历史,而带箭头的部分就是我们最需要的事件,从图中可以看到箭头上的说明是事件的信号,箭头的方向是指示了事件的发送者到接收者。
一般事件信号的定义和事件类型的定义都会放在一个公共的头文件中,因为事件是用来交互的,他会涉及很多的活动对象,所以共享是这些是必要的,例如我们的应用中,事件信号的枚举,事件结构体的定义。这里都放在了dpp.h中:
除了这些内容以外,共享的宏,以及构造函数的声明也要放到这个文件中,因为main.c中要显式的调用这些构造函数。
在main函数中,你需要给每个活动对象创建一个事件队列,给动态申请的事件创建内存池,给发行订阅机制初始化队列,如下:
定义好了事件池以后,就要构造活动对象、初始化系统框架、初始化发行订阅机制、初始化事件池、初始化板级资源,如下:
接下来,启动活动对象,并把系统控制权交回给QF,如下:
到这里我们的应用也就真正的运行起来了。