消息总线设计系列之 - 调停者模式

      自从写了上一篇 消息总线设计系列之 - 观察者模式之后,已经好长时间了,实在惭愧,现在抽出时间了,写下这一篇。观察者模式的特点是每个观察者对象可以定阅一个或多个不同类型的主题对象,每个主题对象包括一个或多个相同类型的观察者对象,他们之间是高度耦合并且直接进行通讯。 如果项目中用到大量的观察者模式之后,你会发现系统中的观察者和主题对象形成了一张错乱无章的关系网,非常难以维护。调停者模式就是为了解决这种错乱无章的对象网之间的通讯问题的,屏蔽了对象之间的直接通讯功能,并充当了转发和路由能能。(为了更清楚的理解这篇文章,你可以先看看上一篇文章 消息总线设计系列之 - 观察者模式
      总线接口设计的要素是:
      1. 总线接口应该包含一个主题的字典集合,字典的键就是主题事件的类型。
      2. 消息发送(通过消息对象可以得到消息类型,然后通过消息类型取出主题对象,然后发送操作就路由到主题对象上)
      3. 总线接口应该提供一个主题对象的一个泛型工厂,方便用户注册观察者

      消息总线接口设计如下:
     public   interface  IMessageRouter
    {
        
///   <summary>
        
///  发送消息
        
///   </summary>
        
///   <param name="message"></param>
         void  Send( object  message);
        
///   <summary>
        
///  得到或注册一个指定消息的主题对象(主题对象的泛型工厂)
        
///   </summary>
        
///   <typeparam name="Message"></typeparam>
        
///   <returns></returns>
        Subject < Message >  Subject < Message > ();
    }
     为了适应消息总线,咱们需要把 上一篇介绍的观察者模式的代码稍微修改一下
   
     ///   <summary>
    
///  抽象主题
    
///   </summary>
     public   abstract   class  Subject
    {
        
///   <summary>
        
///  观察者委托
        
///   </summary>
         public   abstract  Delegate Delegate {  get ;}
        
// 委托参数类型
         public   abstract  Type MessageType {  get ;}
    }

    
public   class  Subject < Message >  : Subject
    {
        
//  利用委托对象做为 观察者列表
         private  ObserverDelegate < Message >  observerList;
        
///   <summary>
        
///  注册或移除观察者
        
///   </summary>
         public   event  ObserverDelegate < Message >  Observers
        {
            add 
// 注册观察者
            {
                observerList 
+=  value;
            }
            remove
// 移除观察者
            {
                observerList 
-=  value;
            }
        }
        
///   <summary>
        
///  观察者委托
        
///   </summary>
         public   override  Delegate Delegate {  get  {  return  observerList; } }
        
///   <summary>
        
///  委托参数类型
        
///   </summary>
         public   override  Type MessageType {  get  {  return   typeof (Message);}}
    }

      准备工作已经到位了,下面就一步一步介绍实现方案了
      1. 构造一个主题字典对象
      
Dictionary < Type, Subject >  subjectList  =   new  Dictionary < Type, Subject > ();
     2. 实现主题的泛型工厂方法
         public  Subject < Message >  Subject < Message > ()
        {
            Type msgType 
=   typeof (Message);
            Subject
< Message >  subject  =   null ;
            
if  ( ! subjectList.ContainsKey(msgType))
            {
                subject 
=   new  Subject < Message > ();
                subjectList[msgType] 
=  subject;
            }
            
else
            {
                subject 
=  subjectList[msgType]  as  Subject < Message > ;
            }

            
return  subject;
        }
      3. 实现消息发送方法
         public   void  Send( object  message)
        {
            
// 空消息直接返回
             if  (message  ==   null )
                
return ;

            
// 取出消息类型
            Type msgType  =  message.GetType();

            
// 根据消息类型得到主题对象
            Subject subject  =  subjectList[msgType];

            
if  (subject  ==   null )
            {
                
return ;
            }

            
// 如果没有观察者来定阅了,就移除该主题对象
             if (subject.Delegate  ==   null )
            {
                subjectList.Remove(msgType);
                
return ;
            }

            
// 取出主题对象的所有观察者集合
            Delegate[] observers  =  subject.Delegate.GetInvocationList();
            
            
// 给所有观察者对象发通知
             foreach  (Delegate observe  in  observers)
            {
                
try
                {
                    observe.DynamicInvoke(message);
                }
                
catch  (Exception ex)
                {
                    Trace.WriteLine(ex.Message);
                }
            }
        }
      我们已完成消息总线的所有设计,下面看看简单的应用,消息总线常常做为一个公共类即全局对象,所以咱们写一个单例模式的消息总线,该总线继承于上面所实现的,代码如下:
     class  LocalMessageRouter:MessageRouter
    {
        
private  LocalMessageRouter(){}
        
public   static   readonly  IMessageRouter Instance  =   new  LocalMessageRouter();
    }

测试代码:
     class  Sample5:ICommand
    {
        
public   void  Execute()
        {
            LocalMessageRouter.Instance.Subject
< string > ().Observers  += new  ObserverDelegate < string > (OnHelloString);
            LocalMessageRouter.Instance.Subject
< string > ().Observers  +=   new  ObserverDelegate < string > (OnHelloString2);
            LocalMessageRouter.Instance.Subject
< string [] > ().Observers  +=   new  ObserverDelegate < string [] > (OnHelloStringArray);
            LocalMessageRouter.Instance.Subject
< Message > ().Observers  +=   new  ObserverDelegate < Message > (OnMessage);

            LocalMessageRouter.Instance.Send(
" ZhangSan " );
            LocalMessageRouter.Instance.Send(
new   string [] {  " LiSi " " ZhangSan "  });
            LocalMessageRouter.Instance.Send(
new  Message( " 李四 " " 张三 " ));
        }
            

        
private   class  Message
        {
            
public   readonly   string  Sender;
            
public   readonly   string  To;

            
public  Message( string  sender,  string  to)
            {
                
this .Sender  =  sender;
                
this .To  =  to;
            }
        }

        
static   void  OnMessage(Message e)
        {
            Console.WriteLine(e.Sender 
+   "  Hello  "   +  e.To);
        }

        
static   void  OnHelloString2( string  e)
        {
            Console.WriteLine(
" 你好  "   +  e);
        }

        
static   void  OnHelloStringArray( string [] e)
        {
            Console.WriteLine(e[
0 +   "  hello  "   +  e[ 1 ]);
        }

        
static   void  OnHelloString( string  e)
        {
            Console.WriteLine(
" Hello  "   +  e);
        }
    }

测试结果:
Hello ZhangSan
你好 ZhangSan
LiSi hello ZhangSan
李四 Hello 张三

下面在给出一个用消息总线模拟实现的IM截图
消息总线设计系列之 - 调停者模式_第1张图片

最后附上 源代码,下一篇介绍怎样彻底解决委托与事件的内存泄漏问题

你可能感兴趣的:(设计)