读书人

《模式工程化实现及扩展》(设计模

发布时间: 2012-11-04 10:42:41 作者: rapoo

《模式——工程化实现及扩展》(设计模式C# 版)《中介者模式》——“自我检验" 参考答案
转自:《模式——工程化实现及扩展》(设计模式C# 版)http://www.cnblogs.com/callwangxiang/

?

?

?

?

?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各对象的职责中独立出来,并用单元测试验证不同的流转过程。

?

?

文件对象的定义

?

class?Document
{
????#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时全部采用事件作为提供给外中介者协调响应关系的入口。

?

增加如下类型

class?DocumentEventArgs?:?EventArgs
{
????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>
///?测试手工定义事件中介者的交互关系
///?</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窗口

------?Test?started:?Assembly:?Mediator.Tests.dll?------

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>
///?测试手工定义事件中介者的交互关系
///?</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);
}

?

?

Output窗口

?

------?Test?started:?Assembly:?Mediator.Tests.dll?------

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?EventMediatorBuilder
{
????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;
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类型定义

?

namespace?Scenario1
{
????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(

读书人网 >软件开发

热点推荐