订阅发布模型定义了一对多的依赖关系,允许多个读者对象同时接收一个主题对象。 当您的状况发生变化时,此主题对象将通知所有订阅者对象,并且您可以自动更新其状况。
将系统划分为一系列相互协作的类具有以下副作用:必须保持适当对象之间的一致性,这会给维护、扩展和重用带来不便。 如果更改一个对象需要同时更改其他对象,并且不知道需要更改的具体对象数量,则可以使用订阅发布模式。
抽象模型有两个方面,其中一个依赖于另一个。 在这种情况下,订阅发布模型可以将这两个模型封装在独立的对象中,每个对象都可以独立修改和重用。 subscription model所做的工作其实就是解除耦合。 通过使耦合双方依赖抽象而不是具体,使各自的变化不影响对侧的变化。
我们日常编写程序时,经常遇到以下情况。
public void上发生了警告消息(
{
更新接口(;
更新数据库(;
向管理员发送邮件();
..
() ) ) ) )。
发生报警信息时,依次执行界面更新()、数据库更新()、管理员邮件)等操作。 表面上代码写得很好,但实际上这里面有很多问题:
首先,这完全是针对流程开发,不适合大型项目。 第二,代码维护量太大。 想象一下,如果在发出警告后执行10个以上的操作,那将是多么大多么复杂的类。 随着时间的推移,开发者自己可能也不知道如何维护。 第三,可扩展性差。 报警后,如何添加语音提示()功能? 是的。 只能添加到名为报警信息生成()的函数中。 这样做会违反“开放-封闭原则”。 然后,在修改原函数的情况下,在测试时,除了测量新功能外,还进行原功能的回归测试; 对于大型项目,进行回归测试可能需要大约两周的时间。 此外,前提是新功能不影响原始功能,并出现新错误。 那么,如何将产生报警信息的()函数与其他函数解耦呢? 别着急,我来介绍一下今天的主角——订阅发布模式。 请参阅下图:
上述流程是对名为报警信息生成() )的函数的描述。 我们应该做的是解除报警和它需要通知的事件的耦合,使它们之间没有相互依赖关系。 解耦图如下。
事件发生者被抽象化,称为消息发行者,即图中的p。 事件的接收被抽象化,称为消息订阅者,即图中的s。 p和s之间通过S.P,也就是订阅进行连接。 这实现了p和s之间的去耦。 首先,p向指定的读者发送消息。 从头到尾,不知道将消息发送到哪个s,也不关心。 如果s想接收消息,则必须注册订阅程序。 注册成功后,s就可以接收来自S.P的消息了。 从头到尾,s不知道是来自哪个具体p的消息,也不关心。 同样,s也可以对S.P执行取消保留操作,如果取消保留成功,s将无法接收来自指定S.P的消息。 这样,p和s之间的去耦被完美解决。
等等,好像还有一个问题。 从图中可以看出,p和s之间已经完成了谅解结合,但p和S.P,s和S.P之间不是又结合了吗? 其实这个问题很容易解决。 想想前篇的装饰图案是如何解除结合的。 是的,关于接口。 这里也使用接口解决p和S.P、s和S.P之间的解耦,同时使用delegate解决多订阅多发布的机制。
实现代码如下所示。 订阅发放模式涉及p、S.P和s三个内容,因此代码多且长。 各位,请耐心阅读。
首先,必须定义两个接口文件以实现p和S.P、s和S.P之间的去耦
ISubscribe.cs
namespacetjvictor.design pattern.subscribe publish
{
//定义订阅事件
publicdelegatevoidsubscribehandle (stringstr;
//定义订阅接口
公共界面isubscribe
{
eventsubscribehandlesubscribeevent;
() ) ) ) )。
() ) ) ) )。
IPublish
namespacetjvictor.design pattern.subscribe publish
{
//定义释放事件
publicdelegatevoidpublishhandle (stringstr;
//定义释放接口
公共接口I publish
{
event PublishHandle PublishEvent;
voidnotify(stringstr;
() ) ) ) )。
() ) ) ) )。
然后设计订阅吧。 很明显,订户要实现双向去耦,必须继承上面的两个接口。 因此,我不用接口来使用抽象类(类是单一继承的)。
namespacetjvictor.design pattern.subscribe publish
{
公共类子组件
t : ISubscribe, IPublish{
private string _subName;
public SubPubComponet(string subName)
{
this._subName = subName;
PublishEvent += new PublishHandle(Notify);
}
#region ISubscribe Members
event SubscribeHandle subscribeEvent;
event SubscribeHandle ISubscribe.SubscribeEvent
{
add { subscribeEvent += value; }
remove { subscribeEvent -= value; }
}
#endregion
#region IPublish Members
public PublishHandle PublishEvent;
event PublishHandle IPublish.PublishEvent
{
add { PublishEvent += value; }
remove { PublishEvent -= value; }
}
#endregion
public void Notify(string str)
{
if (subscribeEvent != null)
subscribeEvent.Invoke(string.Format("消息来源{0}:消息内容:{1}", _subName, str));
}
}
}
接下来是设计订阅者S。S类中使用了ISubscribe来与S.P进行解耦。代码如下:
namespace TJVictor.DesignPattern.SubscribePublish
{
public class Subscriber
{
private string _subscriberName;
public Subscriber(string subscriberName)
{
this._subscriberName = subscriberName;
}
public ISubscribe AddSubscribe { set { value.SubscribeEvent += Show; } }
public ISubscribe RemoveSubscribe { set { value.SubscribeEvent -= Show; } }
private void Show(string str)
{
Console.WriteLine(string.Format("我是{0},我收到订阅的消息是:{1}", _subscriberName, str));
}
}
}
最后是发布者P,继承IPublish来对S.P发布消息通知。
namespace TJVictor.DesignPattern.SubscribePublish
{
public class Publisher:IPublish
{
private string _publisherName;
public Publisher(string publisherName)
{
this._publisherName = publisherName;
}
private event PublishHandle PublishEvent;
event PublishHandle IPublish.PublishEvent
{
add { PublishEvent += value; }
remove { PublishEvent -= value; }
}
public void Notify(string str)
{
if (PublishEvent != null)
PublishEvent.Invoke(string.Format("我是{0},我发布{1}消息", _publisherName, str));
}
}
}
至此,一个简单的订阅发布模式已经完成了。下面是调用代码及运行结果。调用代码模拟了图2中的订阅发布关系,大家可以从代码,运行结果和示例图三方面对照着看。
#region TJVictor.DesignPattern.SubscribePublish
//新建两个订阅器
SubPubComponet subPubComponet1 = new SubPubComponet("订阅器1");
SubPubComponet subPubComponet2 = new SubPubComponet("订阅器2");
//新建两个发布者
IPublish publisher1 = new Publisher("TJVictor1");
IPublish publisher2 = new Publisher("TJVictor2");
//与订阅器关联
publisher1.PublishEvent += subPubComponet1.PublishEvent;
publisher1.PublishEvent += subPubComponet2.PublishEvent;
publisher2.PublishEvent += subPubComponet2.PublishEvent;
//新建两个订阅者
Subscriber s1 = new Subscriber("订阅人1");
Subscriber s2 = new Subscriber("订阅人2");
//进行订阅
s1.AddSubscribe = subPubComponet1;
s1.AddSubscribe = subPubComponet2;
s2.AddSubscribe = subPubComponet2;
//发布者发布消息
publisher1.Notify("博客1");
publisher2.Notify("博客2");
//发送结束符号
Console.WriteLine("".PadRight(50,'-'));
//s1取消对订阅器2的订阅
s1.RemoveSubscribe = subPubComponet2;
//发布者发布消息
publisher1.Notify("博客1");
publisher2.Notify("博客2");
//发送结束符号
Console.WriteLine("".PadRight(50, '-'));
#endregion
#region Console.ReadLine();
Console.ReadLine();
#endregion
运行结果图:
如需转载,请注明本文原创自CSDN TJVictor专栏:http://blog.csdn.net/tjvictor