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

      自从写了上一篇消息总线设计系列之 - 观察者模式之后,已经好长时间了,实在惭愧,现在抽出时间了,写下这一篇。观察者模式的特点是每个观察者对象可以定阅一个或多个不同类型的主题对象,每个主题对象包括一个或多个相同类型的观察者对象,他们之间是高度耦合并且直接进行通讯。 如果项目中用到大量的观察者模式之后,你会发现系统中的观察者和主题对象形成了一张错乱无章的关系网,非常难以维护。调停者模式就是为了解决这种错乱无章的对象网之间的通讯问题的,屏蔽了对象之间的直接通讯功能,并充当了转发和路由能能。(为了更清楚的理解这篇文章,你可以先看看上一篇文章消息总线设计系列之 - 观察者模式
      总线接口设计的要素是:
      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截图
IM.JPG

最后附上源代码,下一篇介绍怎样彻底解决委托与事件的内存泄漏问题
posted @ 2008-05-16 17:44  风云  阅读(6785)  评论(11编辑  收藏  举报