我的Workflow之旅--自己写简单workflow(一)

写这个文章是08年为了给以前公司的一次工作流知识的培训,现在拿出来给大家分享分享。有很多不足之处,请见谅。

•首先我们来看看Workflow历史:

  工作流技术发端于 1970 年代中期办公自动化领域的研究工作,但工作流思想的出现还应该更早, 1968 年 Fritz Nordsieck 就已经清楚地表达了利用信息技术实现工作流程自动化的想法。 1970 年代与工作流有关的研究工作包括:宾夕法尼亚大学沃顿学院的 Michael D. Zisman 开发的原型系统 SCOOP ,施乐帕洛阿尔托研究中心的 Clarence A. Ellis 和 Gary J. Nutt 等人开发的 OfficeTalk 系列试验系统,还有 Anatol Holt 和 Paul Cashman 开发的 ARPANET 上的“监控软件故障报告”程序。 SCOOP, Officetalk 和 Anatol Holt 开发的系统都采用 Petri 网的某种变体进行流程建模。其中 SCOOP 和 Officetalk 系统,不但标志着工作流技术的开始,而且也是最早的办公自动化系统。

  1970 年代人们对工作流技术充满着强烈乐观情绪,研究者普遍相信新技术可以带来办公效率的巨大改善,然而这种期望最终还是落空了。人们观察到这样一种现象,一个成功的组织往往会在适当的时候创造性的打破标准的办公流程;而工作流技术的引入使得人们只能死板的遵守固定的流程,最终导致办公效率低和人们对技术的反感。 1970 年代工作流技术失败的技术原因则包括:在办公室使用个人计算机尚未被社会接受,网络技术还不普遍,开发者还不了解群件技术的需求与缺陷。

  含有工作流特征的商用系统的开发始于 1983 年至 1985 年间,早期的商用系统主要来自于图像处理领域和电子邮件领域。图像处理许多时候需要流转和跟踪图像,工作流恰好迎合这种需求;增强的电子邮件系统也采用了工作流的思想,把原来点对点的邮件流转改进为依照某种流程来流转。在这些早期的工作流系统中只有少数获得了成功。

  进入 1990 年代以后,相关的技术条件逐渐成熟,工作流系统的开发与研究进入了一个新的热潮。据调查,截至 1995 年共有 200 多种软件声称支持工作流管理或者拥有工作流特征。工作流技术被应用于电讯业、软件工程、制造业、金融业、银行业、科学试验、卫生保健领域、航运业和办公自动化领域。

 

•在你做系统时候你否会考虑到以下问题呢?

  1)所谓交互式程序就是程序在执行期间依赖外部实体的刺激,并对此做出响应。而这些交互式程序都将花费大量的时间在等待这些刺激。

  2)怎么简单而清晰的保证控制流正确性。

  对于这两个问题大家也许首先会想到用线程来解决这些:

•线程的灵活性

     用下面的代码来模拟用户交互

代码
class Program
{
static void Main(string
[] args)
{
Console.WriteLine(
"you key is:ok"
);
if(Console.ReadLine()=="ok"
)
Console.WriteLine(
"hello ,will come"
);

Thread.Sleep(
new TimeSpan(0, 0, 5
));
}
}

这段代码是程序主程序一直处于了等待状态,这样干不了别的活咯。现在就得请线程这位大哥来帮忙,让等待脱离主线程,怎么做呢?

代码
public class AsyncDemo
{
public AsyncDemo()
{
m_delegate
= new runDelegate(Run);
}

private delegate string runDelegate();
private runDelegate m_delegate;
public System.IAsyncResult BeginReadLine(System.AsyncCallback asynaCallback, object state)
{
Console.WriteLine(
"BeginReadLine当为前线程为: " + Thread.CurrentThread.GetHashCode());
return m_delegate.BeginInvoke(asynaCallback, state);
}

public string EndReadLine(System.IAsyncResult ar)
{
if (ar == null)
throw new NullReferenceException("参数未空");
Console.WriteLine(
"EndReadLine当为前线程为: " + Thread.CurrentThread.GetHashCode());
return m_delegate.EndInvoke(ar);

}

public string Run()
{
Console.WriteLine(
"Run当为前线程为: " + Thread.CurrentThread.GetHashCode());
return Console.ReadLine();
}
}

class Program
{

static void Main(string[] args)
{
AsyncDemo demo
= new AsyncDemo();
string key = new Random().Next(100).ToString();
Console.WriteLine(
"you key is:" + key);
IAsyncResult result
= demo.BeginReadLine(null, null);
//主程序继续干它的活。。
for(int i=0; i<10;i++)
{
//睡觉。。。。
Thread.Sleep(new TimeSpan(0, 0, 1));
}
//主程序睡觉的某个时候 我们输入了key 马上就拿到了
string s = demo.EndReadLine(result);

if (key.Equals(s))
Console.WriteLine(
"hello, will come!");

Thread.Sleep(
new TimeSpan(0,0,5));
}
}

 

 

用异步委托来开启新的线程来等待交互的输入值。ok,搞定,我们没有让主程序干它不想干的(等待)。它做了喜欢做的睡觉,睡醒后它就拿到了交互输入的key值。

  但是很快我们发现一大堆代码都需要在主程序里关联,交互多就越来越复杂啦。我也不知道那个在什么时候就完成交互,怎么办呢? 脑袋一闪,把key能全局访问,再给他回调函数,不就ok了么,赶紧看看:

 

代码
public static class AsyncDemo
{
static AsyncDemo()
{
m_delegate
= new runDelegate(Run);
}

private delegate string runDelegate();
private static runDelegate m_delegate;
public static System.IAsyncResult BeginReadLine(System.AsyncCallback asynaCallback, object state)
{
Console.WriteLine(
"BeginReadLine当为前线程为: " + Thread.CurrentThread.GetHashCode());
return m_delegate.BeginInvoke(asynaCallback, state);
}

public static string EndReadLine(System.IAsyncResult ar)
{
if (ar == null)
throw new NullReferenceException("参数未空");
Console.WriteLine(
"EndReadLine当为前线程为: " + Thread.CurrentThread.GetHashCode());
return m_delegate.EndInvoke(ar);

}

public static string Run()
{
Console.WriteLine(
"Run当为前线程为: " + Thread.CurrentThread.GetHashCode());
return Console.ReadLine();
}
}

class Program
{
static string key;
static void Main(string[] args)
{
key
= new Random().Next(100).ToString();
Console.WriteLine(
"here you key:" + key);
Console.WriteLine(
"Main当为前线程为: " + Thread.CurrentThread.GetHashCode());
//用户交互结束后直接 按原计划ContinueAt处理
AsyncDemo.BeginReadLine(ContinueAt, null);
//我继续干我的活。。


Thread.Sleep(Timeout.Infinite);
}

static void ContinueAt(IAsyncResult ar)
{
string s = AsyncDemo.EndReadLine(ar);
Console.WriteLine(
"ContinueAt当为前线程为: " + Thread.CurrentThread.GetHashCode());
if (key.Equals(s))
Console.WriteLine(
"hello, will come!");
Thread.Sleep(
new TimeSpan(0, 0, 5));
Environment.Exit(
0);
}
}

  我们通过异步调用将程序逻辑拆分成几块,又连接在一起。 比较:原来程序中key是一个基于栈的局部变量,现在,key是一个静态字段,它具有跨方法的可见性。对于强类型的数据,这种栈无关的方法是降低程序对给定线程的依赖性的关键。
  启发:ContinueAt方法的委托运行起来活似一个书签(Bookmark)------一个逻辑定位点,当受到适当的外部刺激后,程序可以从这个位置恢复执行。

•我们就来尝试做——书签

  怎么做呢?

  1):我们可以给书签命名,还可以为书签提供一个管理器。

  2):这个书签因该可以序列化,可以从持久存储介质存取书签。

  3):我们可以编写一个监听器程序,需要分发到书签去的数据必须由这个程序来分发。

不管那么多了,先上代码:

 

代码
public delegate void BookmarkLocation(Bookmark resumed);

[Serializable]
public class Bookmark
{
private string _Name;
private object _Payload;
private BookmarkManager _BookmarkManager;
private BookmarkLocation _CountinueAt;
public Bookmark(string name,BookmarkLocation countinueAt)
{
this._Name = name;
this._CountinueAt = countinueAt;
}
public string Name
{
get { return _Name; }
}
public BookmarkLocation CountinueAt
{
get { return _CountinueAt; }
}
public object Payload
{
get { return _Payload; }
set { _Payload = value; }
}

public BookmarkManager BookmarkManager
{
get { return _BookmarkManager; }
set { _BookmarkManager = value; }
}


}

public static class BookmarkStorage
{
static BookmarkStorage()
{
_marks
=new List<Bookmark>();
}
private static List<Bookmark> _marks;
public static List<Bookmark> Bookmarks
{
get { return _marks; }
}
}

public class BookmarkManager
{
public BookmarkManager()
{
}
public void Add(Bookmark bookmark)
{
if (bookmark != null)
{
bookmark.BookmarkManager
= this;
BookmarkStorage.Bookmarks.Add(bookmark);
}
}
public void Remove(Bookmark bookmark)
{
BookmarkStorage.Bookmarks.Remove(bookmark);
}
public void Resume(string bookmarkNmae, Object payload)
{
Bookmark mark
= BookmarkStorage.Bookmarks.Where(a => a.Name == bookmarkNmae).FirstOrDefault();
if(mark!=null)
{
mark.Payload
=payload;

//mark.CountinueAt.Invoke(mark);//直接调用
Console.WriteLine(DateTime.Now.ToString() +"Resume当为前线程为: " + Thread.CurrentThread.GetHashCode());
IAsyncResult result
= mark.CountinueAt.BeginInvoke(mark, null, payload);
//result.AsyncWaitHandle.WaitOne();//等待线程同步
Console.WriteLine(DateTime.Now.ToString() +"Resume当为前线程为: " + Thread.CurrentThread.GetHashCode());
//mark.CountinueAt.EndInvoke(result);
}
}
}

[Serializable]
public class OpenSesame
{
string key;
public void Star(BookmarkManager mgr)
{
key
= new Random().Next(100).ToString();
Console.WriteLine(
"here you key:" + key);
Console.WriteLine(
"Star当为前线程为: " + Thread.CurrentThread.GetHashCode());
mgr.Add(
new Bookmark("read",CountionAt));

}

void CountionAt(Bookmark resumed)
{
Thread.Sleep(
new TimeSpan(0, 0, 2));
string s = resumed.Payload as string;
BookmarkManager mgr
= resumed.BookmarkManager;
mgr.Remove(resumed);
Console.WriteLine(DateTime.Now.ToString()
+ "CountionAt当为前线程为: " + Thread.CurrentThread.GetHashCode());
if (key.Equals(s))
Console.WriteLine(
"hello, will come!");

}
}

class Program
{
static void Main(string[] args)
{
//创建书签管理器
BookmarkManager mgr = new BookmarkManager();
OpenSesame pg
= new OpenSesame();
Console.WriteLine(
"Main当为前线程为: " + Thread.CurrentThread.GetHashCode());
//填入书签
pg.Star(mgr);

//.........
//
string str = Console.ReadLine();
//调用书签
mgr.Resume("read", str);
Console.WriteLine(
"Main当为前线程为: " + Thread.CurrentThread.GetHashCode());
//.....
Thread.Sleep(new TimeSpan(0, 0, 5));
}
}

 

 

  哈哈,我们终于完成我们的疑问1。但是我们的交互多了逻辑依然不是很清楚。就不能简单而清晰的保证控制流正确性。

上述代码是用于单个程序实例的,怎么来管理各种类似的程序呢?

  1):需要可复用的组件来封装可恢复语句  

  2):想需要一个很好的控制流逻辑来控制它们。

动手看看再说:

代码
namespace MyWorkflow
{
/// <summary>
/// 执行入口点
/// </summary>
[Serializable]
public abstract class Active
{
string _name;
public Active()
{
_name
= "Active";
}
public Active(string name)
{
_name
= name;
}

public string Name { get {return _name ;} }
public abstract void Execute(BookmarkManager mgr);
}
}

 

 

用Active代表交互行为,我们首先得封装交互的行为,把它自己作为书签,让后给他一个抽象Execute方法来执行处理。需要扩展书签管理

 

代码
namespace MyWorkflow
{
public delegate void BookmarkLocation(Bookmark resumed);

[Serializable]
public class Bookmark
{
private string _Name;
private object _Payload;
private BookmarkManager _BookmarkManager;
private BookmarkLocation _CountinueAt;
public Bookmark() { }
public Bookmark(string name, BookmarkLocation countinueAt)
{
this._Name = name;
this._CountinueAt = countinueAt;
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
public BookmarkLocation CountinueAt
{
get { return _CountinueAt; }
set { _CountinueAt = value; }
}
public object Payload
{
get { return _Payload; }
set { _Payload = value; }
}

public BookmarkManager BookmarkManager
{
get { return _BookmarkManager; }
set { _BookmarkManager = value; }
}


}


public class BookmarkManager
{
private List<Bookmark> Bookmarks;
private List<Bookmark> internalBookmarks;
public BookmarkManager()
{
Bookmarks
= new List<Bookmark>();
internalBookmarks
=new List<Bookmark>();
}
public void Add(Bookmark bookmark)
{
if (bookmark != null)
{
bookmark.BookmarkManager
= this;
Bookmarks.Add(bookmark);
}
}
public void Remove(Bookmark bookmark)
{
Bookmarks.Remove(bookmark);
}

internal void Resume(string bookmarkNmae, Object payload)
{
Bookmark mark
= Bookmarks.Where(a => a.Name == bookmarkNmae).FirstOrDefault();

if (mark != null)
{
mark.Payload
= payload;
//mark.CountinueAt.Invoke(mark);//直接调用
IAsyncResult result = mark.CountinueAt.BeginInvoke(mark, null, payload);
mark.CountinueAt.EndInvoke(result);
}
Bookmarks.Remove(mark);
}

public void Done()
{
foreach(Bookmark mark in internalBookmarks)
{
IAsyncResult result
= mark.CountinueAt.BeginInvoke(mark, null, mark.Payload);
mark.CountinueAt.EndInvoke(result);
}
internalBookmarks.Clear();
}

public void RunActive(Active active, BookmarkLocation continueAt)
{
Bookmark mark
= new Bookmark{ Name= active.Name, CountinueAt= continueAt, BookmarkManager=this, Payload=active };
internalBookmarks.Add(mark);
active.Execute(
this);
}
}
}

 

 

 

接下来看看怎么实现以前的输入输出的交互呢

代码
namespace MyWorkflow
{
public class Read : Active
{
public Read()
:
base("Read") { }

private string _Text;
public string Text { get { return _Text; } }

public override void Execute(BookmarkManager mgr)
{
mgr.Add(
new Bookmark(Name, null));
}

public void ContinueAt(Bookmark resumed)
{
_Text
= resumed.Payload as string;

BookmarkManager mgr
= resumed.BookmarkManager;
mgr.Remove(resumed);
mgr.Done();
}
}

class OpenSesame:Active
{
string key;

public override void Execute(BookmarkManager mgr)
{
key
= new Random().Next(100).ToString();
Console.WriteLine(
"here you key:" + key);
mgr.RunActive(
new Read(), null);
}

public void ContinueAt(Bookmark resumed)
{
Read read
=resumed.Payload as Read;
if (read.Text == key)
Console.WriteLine(
"hello,will come");

}
}
}

到此我们实现了可恢复语句组件。不知道你看清楚了没有呢?呵呵。。放轻松,仔细看看,这里有点绕头啊。。

我们怎么去做控制流呢?呵呵,休息休息。请见下文。。

posted @ 2010-05-23 16:46  木吉他-.-  阅读(526)  评论(0编辑  收藏  举报