- 浏览: 3309 次
- 性别:
- 来自: 北京
最新评论
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#的工程化实现及扩展
690页完整的《设计模式:基于C#的工程化实现及扩展 》王翔著作 pdf版
### 设计模式——基于C#的工程化实现及扩展 #### 第一部分:预备知识——发掘用C#语言进行面向对象化设计的潜力 ##### 第1章 重新研读C#语言 **1.1 说明** 本章旨在帮助读者更好地理解和掌握C#语言的核心特性...
《设计模式:基于C#的工程化实现及扩展》是由王翔编著的一本深入探讨设计模式在C#编程中的应用的专业书籍。这本书的核心在于如何将经典的23种设计模式有效地融入到C#的工程实践中,同时探讨了如何对这些模式进行扩展...
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者王翔通过C#语言来阐述各种设计模式的工程化应用和拓展。设计模式是软件开发中的一种最佳实践,它总结了在特定场景下为解决常见问题而...
面向对象设计模式,也就是本书简称的“设计模式”,是软件实际...模式的主要价值在于它们是以往经验的浓缩,尤其在我们建立复杂系统的时候,借鉴个采用模式可以让我们少走弯路,其设计比较灵活并且具有不错的扩展性。
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者为王翔。本书的核心目标是通过C#语言,系统地介绍和讲解设计模式在实际工程中的应用与拓展。设计模式是软件开发中经过实践验证的、可...
《设计模式--基于C#的工程化实现及扩展》是一本深入探讨软件设计模式的书籍,作者王翔通过C#语言来阐述各种设计模式的工程化应用和拓展。设计模式是软件开发中的一种最佳实践,它总结了在解决特定问题时,经过时间和...
设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的经验总结。"设计模式之美——教你写出高质量代码"这个主题旨在帮助开发者更好地理解和应用设计模式,从而提升代码的质量和可维护性。设计模式...
最后,"设计模式——基于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章!
在本文中,将详细探讨23种设计模式中的两种——单件模式(Singleton Pattern)和抽象工厂模式(Abstract Factory Pattern),并以C#语言为例来阐述其在实际开发中的应用和实现。 ### 单件模式(Singleton Pattern)...
《C#设计模式(英文版)》是一本深入探讨C#编程中常用设计模式的专业书籍。设计模式是软件工程中的重要概念,它们是解决常见问题的成熟、可重用的解决方案,可以提升代码的可读性、可维护性和扩展性。这本书通过英文版...
命令模式是一种行为设计模式,它将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在软件工程中,这种模式常用于解耦发出命令的对象(调用者)与...
本篇文档将重点介绍几种在C#实现的设计模式,包括中介者模式、备忘录模式、观察者模式、策略模式和访问者模式。 首先,我们来看中介者模式(Mediator Pattern)。中介者模式用于减少类之间的通信复杂性。在没有中介...