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