我的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)怎么简单而清晰的保证控制流正确性。

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

•线程的灵活性

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

ContractedBlock.gifExpandedBlockStart.gif代码
 
   
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
));
}
}

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

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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了么,赶紧看看:

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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):我们可以编写一个监听器程序,需要分发到书签去的数据必须由这个程序来分发。

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

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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):想需要一个很好的控制流逻辑来控制它们。

动手看看再说:

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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方法来执行处理。需要扩展书签管理

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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 );
}
}
}

 

 

 

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

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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 " );

}
}
}

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

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

转载于:https://www.cnblogs.com/zhouhoujun/archive/2010/05/23/1742047.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值