工作流内部工作原理(二)
接续上一篇,我们会去分离程序和资料。对程序员来说,程序和资料是非常不一样的概念。比如说,你可以执行程序,却不可以执行资料。程序代码在执行过程中不会被修改(除了少数比如hook或者overlay的例子除外),但是资料却没有这样的限制,甚至我们觉得资料常常被修改是再正常不过。对于处理器来说,它们并没有多大的区别,都是一堆bytes。从一个操作系统的角度去看,代码不会修改这一点可以用来优化内存的使用,因为代码只要存一份就够了,可以用来起几个进程。
同样的道理也可以使用在Workflow上。我们做这样的比喻,Sequence就是程序,但是sequenceCounter只不过是执行资料而已。sequenceCounter这个成员定义在Sequence上使它们不可区分。想像同样的一个Sequence有很多个进程在跑,那么sequenceCounter就会乱掉。
读一下Activity这个类的代码,States定义在Activity上其实是个更大的问题。与其让States通过activity去读取,我们会把它变成一个Execute的参数。同样地,NextDelegate也不应该放在Activity上。不过这个问题更麻烦一点,因为Sequence需要同时有两个NextDelegate。去解决这个问题,我们会使用一个Stack<Frame>去存这些NextDelegate物件。勤快的读者们,请你们亲手试一试,因为我们在下一个版本会有很多的改动如下
代碼示例: Version 6
我尝试把Version 6和Version 5的改动减到最少了,在把delegate的存取方式改动的同时,我们也优化了Sequence的执行。现在执行Sequence下的Activity再也不会通过Sequence。现在的代码能跑了,我们就该重构。这次我希望States把它的Stack<Frame>给封装起来。这样这个Stack的逻辑就不会四处分散。说也不是很难,只要把Frame变成private就可以驱动这些重构了,我做的不过就是给操作一个合适的名字而已。
Version 7 没有甚麽实质改动,只是封装一下而已。
代碼示例: Version 7
现在我们已经很接近了,在这个版本里我们只对States做序列化。我们问一下自己,到底我们做完程序和资料的分离没有?答桉是没有,因为States里有Delegate,而delegate又回到Activity物件,即程序。我们可以通过序列化MethodInfo,而非delegate,去达到这一点。我们在序列化MethodInfo的同时,也需要存ActivityID。ActivityID是一个Activity的唯一身份码,我们可以通过深度搜寻去找出来。由于这些代码比较複杂,也没有甚麽太大的技术难点,在这里我们就轻轻带过了。另外一个问题就是我们没有保证程序是唯读的。这问题有多种解决的办法。在WF3中它採取了先拷贝一份,永远拷贝来执行的方法,而WF4则选择做一个拷贝(只拷贝对执行有作用的资料),跑原来的一份,而在跑的时候保证跑的确实是原来的拷贝。我们也可以选择当用户更新程序时让它出错。同样地,这些都比较明显,就略过不写了。
还有另外一个问题,就是一行的程序(比喻为一个Activity)可以被多次执行。最简单的例子就是迴圈。这时候,它们的储存必须定义在Frame上,实际上,所有的资料都应该存在Frame上,这就没有States的全局性了,这是WF4比它前一个版本优胜的地方。
看着States的实现,我们不难发现ScheduleActivity只能被调用一次,因为Frame上只有一个NextDelegate成员。下一篇我们会去改进ScheduleActivity,允许多个未完成的Activity,我们也会讲一讲如何让Workflow和主程序如何使用Bookmark通讯。