《模式——工程化实现及扩展》(设计模式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、验证“分析第一步”的设想

///  
///  测试手工定义事件中介者的交互关系
///  

[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 ).

 

 

///  
///  测试手工定义事件中介者的交互关系
///  

[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;

        
///  
        
///  配置不同协调关系
        
///  实际项目中可以采用本章介绍的基于配置文件的定义方式
        
///  

        [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 " );

        }

        
///  
        
///  测试通过Event Mediator Builder构造现有业务流程下的协作关系
        
///  

        [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 " );
        }

        
///  
        
///  测试通过Event Mediator Builder构造管理层期望的未来业务流程下的协作关系
        
///  

        [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(

你可能感兴趣的:(设计模式,C#,C,C++,配置管理)