- 浏览: 3330 次
- 性别:
- 来自: 北京
最新评论
MarvellousWorks公司有 A、 B、 C三个部门负责文件的拟稿、审批和备案,现有的流程如下:
1. A部门拟稿后将文件报 B部门审核
2. B部门对于文件审核后,确认文件体例没有缺项后就通知 C部门发布
3. 如果 B部门发现文件体例有缺项时,将文件返回给 A部门重新修改
4. C部门在接到 B部门传来的文件时,先再发布,然后对其归档
不过, MarvellousWorks的管理层为了加强部门间文件流转的管理,正在酝酿修改工作流程:
1、 增加 D部门专门负责归档, C部门将归档职责划入 D部门后只负责发布文件
2、 C部门发布文件后也须先在 D部门归档
3、 A、 B部门间所有往复流转的中间文件也都报给 D部门归档
请采用本章介绍的 中介者模式及其扩展处理 ,将文件的流转调度过程从 A、 B、 C、 D各对象的职责中独立出来,并用单元测试验证不同的流转过程。
{
#region essential fields
public string Subject { get ; set ; }
public string Body { get ; set ; }
#endregion
#region optional fields
public string Comment { get ; set ; }
#endregion
public override string ToString()
{
return string .Format( " \n[{0}]\n------------------\n{1}\n({2})\n " , Subject, Body, Comment);
}
}
参考答案
分析第一步
上 述A、B、C、D部门间的协作关系比较复杂,而且预期很快会变化,但协作的中间内容很简单——都是文件,所以采用事件方式,由.NET Framework自己的事件机制作为中介者相对很简单,而且类型间的依赖关系全都推给.NET Framework,为以后扩展更多参与方的协作关系提供便利。
据此,我们定义A、B、C、D时全部采用事件作为提供给外中介者协调响应关系的入口。
增加如下类型
{
Document document;
public DocumentEventArgs(Document document)
{
this .document = document;
}
public Document Document{ get { return document;}}
}
abstract class Department
{
protected Document document = new Document();
public Document Document
{
get { return document;}
protected set { document = value;}
}
}
分析第二步
如果直接通过事件重载操作符 += 和-=建立各Colleague的响应关系,需要重复编写代码,而且不能在系统上线后将这个工作交给管理员维护。
因此,考虑参考前面的Builder模式,增加一个基于配置动态维护维护事件响应关系的对象。
1、验证“分析第一步”的设想
/// 测试手工定义事件中介者的交互关系
/// </summary>
[TestMethod]
public void TestManualDefineEventMediatorInSucceedBranch()
{
// 用事件配置松散的响应关系
a1.WriteDocumentFinishedHandler += b1.OnReceiveFileToReview;
b1.ReviewDocumentFailedHandler += a1.OnReviewFailed;
b1.ReviewDocumentSucceedHandler += c1.OnReceiveFileToPublish;
b1.ReviewDocumentSucceedHandler += c1.OnReceiveFileToArchive;
// 成功的路径
a1.Write( " a " , " b " , " c " );
// 验证修订后的内容曾经流转给了B
Assert.AreEqual < string > ( " a " , b1.Document.Subject);
Assert.AreEqual < string > ( " b " , b1.Document.Body);
Assert.AreEqual < string > ( " c " , b1.Document.Comment);
// 验证修订后的内容也曾经流转给了C
Assert.AreEqual < string > ( " a " , c1.Document.Subject);
Assert.AreEqual < string > ( " b " , c1.Document.Body);
Assert.AreEqual < string > ( " c " , c1.Document.Comment);
}
Output窗口
A begin write
A write finished
[a]
------------------
b
( c )
B received doc from A to review
B begin review
B review succeed
C received doc to publish from B
C published
C received doc to archive from B
C archived
1 passed , 0 failed , 0 skipped , took 0.50 seconds ( MSTest 10.0 ).
/// 测试手工定义事件中介者的交互关系
/// </summary>
[TestMethod]
public void TestManualDefineEventMediatorInFailedBranch()
{
// 用事件配置松散的响应关系
a1.WriteDocumentFinishedHandler += b1.OnReceiveFileToReview;
b1.ReviewDocumentFailedHandler += a1.OnReviewFailed;
b1.ReviewDocumentSucceedHandler += c1.OnReceiveFileToPublish;
b1.ReviewDocumentSucceedHandler += c1.OnReceiveFileToArchive;
// 失败的路径
a1.Write( " a " , "" , "" );
// 验证确实文档曾经流转给了B
Assert.AreEqual < string > ( " a " , b1.Document.Subject);
Assert.AreEqual < string > ( "" , b1.Document.Body);
Assert.AreEqual < string > ( "" , b1.Document.Comment);
// 验证文档并没有流转给C
Assert.IsNull(c1.Document.Subject);
Assert.IsNull(c1.Document.Body);
Assert.IsNull(c1.Document.Comment);
// 修正错误的内容,重新执行流程
a1.Write( " a " , " b " , " c " );
// 验证修订后的内容曾经流转给了B
Assert.AreEqual < string > ( " a " , b1.Document.Subject);
Assert.AreEqual < string > ( " b " , b1.Document.Body);
Assert.AreEqual < string > ( " c " , b1.Document.Comment);
// 验证修订后的内容也曾经流转给了C
Assert.AreEqual < string > ( " a " , c1.Document.Subject);
Assert.AreEqual < string > ( " b " , c1.Document.Body);
Assert.AreEqual < string > ( " c " , c1.Document.Comment);
}
A begin write
A write finished
[a]
------------------
()
B received doc from A to review
B begin review
B review failed
A received doc review failed from B
A begin write
A write finished
[a]
------------------
b
( c )
B received doc from A to review
B begin review
B review succeed
C received doc to publish from B
C published
C received doc to archive from B
C archived
1 passed , 0 failed , 0 skipped , took 3.64 seconds ( MSTest 10.0 ).
2、验证“分析第二部” 的设想
定义管理基于事件的中介关系Builder
{
class ConfigItem
{
public Type SourceType { get ; set ; }
public Type TargetType { get ; set ; }
public string SourceEventName { get ; set ; }
public string TargetHandlerMethodName { get ; set ; }
public override bool Equals( object obj)
{
if (obj == null ) throw new ArgumentNullException( " obj " );
var target = (ConfigItem)obj;
return
SourceType == target.SourceType &&
TargetType == target.TargetType &&
string .Equals(SourceEventName, target.SourceEventName) &&
string .Equals(TargetHandlerMethodName, target.TargetHandlerMethodName);
}
}
IList < ConfigItem > config = new List < ConfigItem > ();
public EventMediatorBuilder AddConfig(Type sourceType, Type targetType, string sourceEventName, string targetHandlerMethodName)
{
if (sourceType == null ) throw new ArgumentNullException( " sourceType " );
if (targetType == null ) throw new ArgumentNullException( " targetType " );
if ( string .IsNullOrEmpty(sourceEventName)) throw new ArgumentNullException( " sourceEventName " );
if ( string .IsNullOrEmpty(targetHandlerMethodName)) throw new ArgumentNullException( " targetHandlerMethodName " );
if (sourceType.GetEvent(sourceEventName) == null ) throw new NotSupportedException(sourceEventName);
var item = new ConfigItem()
{
SourceType = sourceType,
TargetType = targetType,
SourceEventName = sourceEventName,
TargetHandlerMethodName = targetHandlerMethodName
};
if ( ! config.Contains(item))
config.Add(item);
return this ;
}
public EventMediatorBuilder BuildAUpColleagues( params object [] colleagues)
{
if (colleagues == null ) throw new ArgumentNullException( " colleagues " );
if (config.Count() == 0 ) return this ; // 没有通信关系配置项
if (colleagues.Count() == 1 ) return this ; // 没有需要配置的关联对象组
colleagues.ToList().ForEach(x => { if (x == null ) throw new ArgumentNullException(); });
/// / 限制:不支持一类对象的某个实例同时向另一类对象多个实例的通知
// if (colleagues.GroupBy(x => x.GetType()).Count() != colleagues.Count())
// throw new NotSupportedException();
foreach (var item in config)
{
var sources = colleagues.Where(x => x.GetType() == item.SourceType);
if ((sources == null ) || (sources.Count() == 0 ))
continue ;
var targets = colleagues.Where(x => x.GetType() == item.TargetType);
if ((targets == null ) || (targets.Count() == 0 ))
continue ;
var eventInfo = item.SourceType.GetEvent(item.SourceEventName);
if (eventInfo == null )
continue ;
var methodInfo = item.TargetType.GetMethod(item.TargetHandlerMethodName, BindingFlags.Public | BindingFlags.Instance);
if (methodInfo == null )
continue ;
// 绑定事件响应关系
foreach (var source in sources)
foreach (var target in targets)
eventInfo.AddEventHandler(source, Delegate.CreateDelegate(eventInfo.EventHandlerType, target, methodInfo));
}
return this ;
}
}
实现和单元测试验证
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MarvellousWorks.PracticalPattern.Mediator.Tests.Exercise
{
[TestClass]
public class DocumentWorkflowMediatorFixture
{
Scenario1.A a1;
Scenario1.B b1;
Scenario1.C c1;
Scenario2.A a2;
Scenario2.B b2;
Scenario2.C c2;
Scenario2.D d2;
EventMediatorBuilder scenario1Builder;
EventMediatorBuilder scenario2Builder;
/// <summary>
/// 配置不同协调关系
/// 实际项目中可以采用本章介绍的基于配置文件的定义方式
/// </summary>
[TestInitialize]
public void Initialize()
{
a1 = new Scenario1.A();
b1 = new Scenario1.B();
c1 = new Scenario1.C();
a2 = new Scenario2.A();
b2 = new Scenario2.B();
c2 = new Scenario2.C();
d2 = new Scenario2.D();
scenario1Builder = new EventMediatorBuilder()
// 1. A部门拟稿后将文件报B部门审核
.AddConfig( typeof (Scenario1.A), typeof (Scenario1.B), " WriteDocumentFinishedHandler " , " OnReceiveFileToReview " )
// 2. B部门对于文件审核后,确认文件体例没有缺项后就通知C部门发布
.AddConfig( typeof (Scenario1.B), typeof (Scenario1.C), " ReviewDocumentSucceedHandler " , " OnReceiveFileToPublish " )
// 3. 如果B部门发现文件体例有缺项时,将文件返回给A部门重新修改
.AddConfig( typeof (Scenario1.B), typeof (Scenario1.A), " ReviewDocumentFailedHandler " , " OnReviewFailed " )
// 4. C部门在接到B部门传来的文件时,先再发布,然后对其归档
.AddConfig( typeof (Scenario1.C), typeof (Scenario1.C), " DocumentPublishedHandler " , " OnReceiveFileToArchive " );
scenario2Builder = new EventMediatorBuilder()
.AddConfig( typeof (Scenario2.A), typeof (Scenario2.B), " WriteDocumentFinishedHandler " , " OnReceiveFileToReview " )
.AddConfig( typeof (Scenario2.A), typeof (Scenario2.D), " WriteDocumentFinishedHandler " , " OnReceiveFileToArchive " )
.AddConfig( typeof (Scenario2.B), typeof (Scenario2.A), " ReviewDocumentFailedHandler " , " OnReviewFailed " )
.AddConfig( typeof (Scenario2.B), typeof (Scenario2.D), " ReviewDocumentFailedHandler " , " OnReceiveFileToArchive " )
.AddConfig( typeof (Scenario2.B), typeof (Scenario2.C), " ReviewDocumentSucceedHandler " , " OnReceiveFileToPublish " )
.AddConfig( typeof (Scenario2.C), typeof (Scenario2.D), " DocumentPublishedHandler " , " OnReceiveFileToArchive " );
}
/// <summary>
/// 测试通过Event Mediator Builder构造现有业务流程下的协作关系
/// </summary>
[TestMethod]
public void TestScenario1()
{
// 通过Event Mediator以及配置信息建立三个部门Colleague间的协作关系
// 所有协调关系统一剥离到作为Mediator的.NET事件机制上
scenario1Builder.BuildAUpColleagues(a1, b1, c1);
// 成功的路径
Trace.WriteLine( " Succeed path " );
a1.Write( " a " , " b " , " c " );
// 失败的路径
Trace.WriteLine( " \n\nFailed path " );
a1.Write( " a " , "" , "" );
// 修正错误的内容,重新执行流程
Trace.WriteLine( " Modified after review failed path " );
a1.Write( " a " , " b " , " c " );
}
/// <summary>
/// 测试通过Event Mediator Builder构造管理层期望的未来业务流程下的协作关系
/// </summary>
[TestMethod]
public void TestScenario2()
{
// 通过Event Mediator以及配置信息建立三个部门Colleague间的协作关系
// 所有协调关系统一剥离到作为Mediator的.NET事件机制上
scenario2Builder.BuildAUpColleagues(a2, b2, c2, d2);
// 成功的路径
Trace.WriteLine( " Succeed path " );
a2.Write( " a " , " b " , " c " );
// 失败的路径
Trace.WriteLine( " \n\nFailed path " );
a2.Write( " a " , "" , "" );
// 修正错误的内容,重新执行流程
Trace.WriteLine( " Modified after review failed path " );
a2.Write( " a " , " b " , " c " );
}
}
}
备注1:现有情景下的A、B、C类型定义
{
class A : Department
{
public event EventHandler < DocumentEventArgs > WriteDocumentFinishedHandler;
public void Write( string subject, string body, string comment)
{
if (Document == null ) throw new NullReferenceException( " Document " );
Trace.WriteLine( " A begin write " );
Document.Subject = subject;
Document.Body = body;
Document.Comment = comment;
Trace.WriteLine( " A write finished " );
Trace.WriteLine(Document);
if (WriteDocumentFinishedHandler != null )
WriteDocumentFinishedHandler(
相关推荐
设计模式:基于C#的工程化实现及扩展
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者王翔通过C#语言来阐述各种设计模式的工程化应用和拓展。设计模式是软件开发中的一种最佳实践,它总结了在特定场景下解决常见问题的...
总之,《设计模式:基于C#的工程化实现及扩展》是一本对于C#开发者来说非常有价值的参考资料,它不仅讲解了设计模式的理论,还提供了丰富的实践案例,帮助读者掌握如何在实际项目中应用这些模式,提升软件开发的专业...
《设计模式:基于C#的工程化实现及扩展》是由王翔编著的一本深入探讨设计模式在C#编程中的应用的专业书籍。这本书的核心在于如何将经典的23种设计模式有效地融入到C#的工程实践中,同时探讨了如何对这些模式进行扩展...
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者王翔通过C#语言来阐述各种设计模式的工程化应用和拓展。设计模式是软件开发中的一种最佳实践,它总结了在特定场景下为解决常见问题而...
以上只是部分设计模式的概述,实际的《设计模式:基于C#的工程化实现及扩展》可能会涵盖更多内容,如模板方法模式、职责链模式、组合模式等,并详细介绍如何在实际项目中有效应用这些模式。为了获取完整的资料,您...
本资料"设计模式-基于C#的工程化实现及扩展"着重探讨如何在C#编程环境中高效地应用设计模式,并对其进行工程化的实现和扩展。 在C#中,设计模式的应用有助于提升代码的可读性、可维护性和可扩展性。常见的设计模式...
设计模式是软件工程中的一种重要概念,它代表了在特定情境下解决常见问题的最佳实践。刘伟先生在讲解设计模式时,通常会深入浅出地介绍这些模式的原理、应用场景以及如何有效地在实际编程中应用它们。设计模式并不是...
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者王翔通过C#语言来阐述各种设计模式的工程化应用和拓展。设计模式是软件开发中的一种最佳实践,它总结了在解决特定问题时,经过时间和...
本资料“《java设计模式》课后习题模拟试题解答——刘伟.zip”主要涵盖了Java设计模式的学习与应用,特别是针对刘伟教授的相关课程的课后习题及模拟试题的解答。 设计模式分为三大类:创建型、结构型和行为型模式。...
《新版设计模式手册 - C#设计模式(第二版)》是一部深入探讨C#编程中设计模式的权威指南,尤其适合已经有一定C#基础并希望提升软件设计能力的开发者阅读。设计模式是解决软件开发中常见问题的经验总结,是软件工程的...
《C#设计模式PDF 电子书》是一本专注于C#编程语言中设计模式的应用和解析的书籍,2005年04版意味着它在设计模式领域的应用尚处于早期阶段,但仍然具有很高的学习价值。设计模式是软件工程中经过实践验证的、解决常见...
设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的经验总结。"设计模式之美——教你写出高质量代码"这个主题旨在帮助开发者更好地理解和应用设计模式,从而提升代码的质量和可维护性。设计模式...
最后,"设计模式——基于C#的工程化实现及扩展_示例说明.txt"文件提供了对源码的解释和应用场景的描述,这对于初学者来说是极其宝贵的资源。它可以帮助你理解每个模式的意图、适用场景以及如何在C#代码中正确地引入...
设计模式作为软件工程中解决特定问题的典型方案,是每一个有志于成为高级开发者的程序员不可或缺的知识储备。本书在各个章节后都精心安排了习题和模拟试题,意在帮助读者在实践中巩固理论知识,提升解决实际编程问题...
《C#经典设计模式及代码示例》是针对C#开发者深入理解并应用设计模式的一份宝贵资源。设计模式是软件工程中经过实践验证的、解决常见问题的有效模板,能够帮助程序员提高代码的可读性、可维护性和复用性。这本书结合...
介绍软件编程中的设计思想,文中实例均基于C#,适合于有一定基础的编程人员。
前26章!前26章!前26章!前26章!前26章!前26章!前26章!
前26章!前26章!前26章!前26章!前26章!前26章!前26章!
前26章!前26章!前26章!前26章!前26章!前26章!