浮华过后,真金始现

一切问题最终都是时间问题,一切烦恼其实都是自寻烦恼
随笔 - 20, 文章 - 10, 评论 - 92, 阅读 - 83873
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

【插件式框架探索系列】建立基于委托的订阅发布机制

Posted on   Kolor  阅读(2956)  评论(8编辑  收藏  举报

前些时候有个想法,想把自己感觉很有意思且方便平时开发的东西合起来,建立一个Framework,它会让开发变得更得心应手,可惜后来随着更多的东西被加入其中,越来越发觉这可能并不是一个开发者需要的东西,因为它太杂了,是的,作为一个Framework,还是单纯点的好,后来工作也忙了,就停滞了。这几天出差广州,夜来无事,便捡个模块来说道说道。

Messenger

在平时的开发中,数据的传递、处理等可以通过事件机制来完成,但是事件机制会引入耦合,当然这种耦合很多时候是合理的,但是却有那么一些时候这种耦合却不是你所需要的,过多的依赖会让你感到困惑,而引入Messenger就是为了解除这种依赖,请看下图。

clip_image002[4]

Figure 1. 类之间的单向依赖

clip_image004[4]

Figure 2. 类之间的双向依赖

clip_image006[4]

Figure 3. 引入Messenger后的依赖关系

Messenger的概念来自MVVM Foundation项目,在看完该项目的源代码后,发现其中的Messenger是个很好的机制,就借鉴这个机制结合实际情况设计了自己的Messenger。

顾名思义,Messenger在这里作为行为的传递者,其职责是预先接受行为的订阅,在需要的时候执行这些行为,即发布。

clip_image008[4]

Figure 4. Messenger类图

从类图上我们看到Messenger提供了订阅、取消订阅和发布行为的能力。Messenger内置一个数据结构用于存储订阅的行为,存储结构描述为Dictionary<T, List<WeakAction>>,T为行为在语义上的含义,作为字典的Key,其对应一组行为,而每个行为都以WeakAction表示,WeakAction会保证Messenger与定义行为的对象之间保持弱引用(WeakRefernce)关系,避免内存泄漏,如此便构成了行为的存储结构MessageToActionMap。

clip_image010[4]

Figure 5. MessageToActionMap类图

clip_image012[4]

Figure 6. WeakAction类图

终于Messenger拥有了订阅和发布行为的能力,为了提供更好的灵活性,Messenger被定义为泛型抽象类型,具体的Messenger通过继承方式扩展,在此,我们定义了几种典型的Messenger实现:

clip_image014[4]

Firgure 7. Messenger实现

Ø GeneralMessenger

GeneralMessenger作为Messenger的一个通用实现,继承自Messenger<object>,适用于大多数场景。

Ø TypeMessenger

TypeMessenger更适用于基于消息驱动的应用场景,在分布式应用中,系统间会互相传递消息,而消息的接收方往往会依据不同的消息类型作出不同的处理,比如Server收到Client的登录请求时会去执行验证行为,而收到登出请求时会作出释放资源行为。当然所有的这些,GeneralMessenger完全可以胜任,只不过TypeMessenger更加方便,后面提到的数据的订阅发布机制便是使用TypeMessenger实现的。

Ø OnceOffMessenger

OnceOffMessenger作为GeneralMessenger的特殊实现,它最特殊的地方便是订阅的行为,在发布后即消亡,适用于处理一次性的行为。

为了适应各种不同的开发场景,Messenger本身是可以创建多个实例的,如果想实现单例模式,可以通过使用SingletonManager.GetInstance<T>方法获取单例。

clip_image016[4]

Firgure 8. SingletonManager类图

1
TypeMessenger messenger = SingletonManager.GetInstance<TypeMessenger>();

 

数据的订阅发布机制

发布者的职责是提供数据,而订阅者的职责则是消费数据,即处理数据,当然订阅者可以同时也是发布者,如此可以实现数据的再处理。为此定义了发布接口和订阅接口:

clip_image018[4]

Firgure 9. 订阅发布接口类图

同时提供订阅和发布的标准实现:

clip_image020[5]

Firgure 10. 订阅发布标准实现类图

我们可以看到订阅者同时也是发布者,两者皆是使用TypeMessenger实现订阅发布机制。

SubscriberAdapter<T>的Subscribe方法中订阅T的处理行为为Handle方法:

1
2
3
4
5
6
7
/// <summary>
/// Subscribe the message handler.
/// </summary>
public void Subscribe()
{
    messenger.Subscribe<T>(new Action<T>(msg => Handle(msg)));
}

 

而在PublisherAdapter的OnMessage和OnMessageAsyn方法中发布数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <summary>
/// Publish the message.
/// </summary>
/// <param name="message">The message.</param>
public virtual void OnMessage(object message)
{
    if (message != null)
    {
        messenger.NotifyAll(message);
    }
}
 
/// <summary>
/// Publish the message asynchronously.
/// </summary>
/// <param name="message">The message.</param>
public virtual void OnMessageAsyn(object message)
{
    if (message != null)
    {
        messenger.NotifyAllAsyn(message);
    }
}

同时在SubscriberAdapter<T>中也可以以类似的方式发布数据,该数据类型的订阅者会处理该数据。

这种设计方式也是借鉴于Apache MINA框架中的过滤器概念,让数据在一个过滤器链中传递,链上的每个过滤器都有机会对数据进行处理和再加工。不过这里的订阅者的数据发布行为只能发布不同于当前类型的数据,否则会进入无限制的死循环,该行为已经通过重载PublisherAdapter的OnMessage和OnMessageAsyn方法实现。

写在后面

事件机制传递数据很方便,也比较简单,只是个人觉得还是这种方式更加灵活,也更加简单。

下载代码

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示