观察者模式又叫做发布——订阅(Publish/Subscribe)模式。它的概念在我之前的博文中,也多次介绍过。今天,通过一个小Demo,模拟一下项目中使用观察者模式的基本结构。
首先,回顾一下观察者模式的概念。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象。这个主体对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
对于这些概念的东西,我们要结合实例来理解,这里我们联想大话设计模式中的例子:
公司中有看球的观察者A,有炒股票的观察者B;而老板和秘书两个通知者,就是两个主题。观察者A和观察者B同时监视着老板的反映。一旦老板有了新的指示,观察者A和观察者B以及所有的其他观察者,都会受到通知。
老板不关心具体谁会收到通知,他只管发通知;而且每个观察者之间也相对独立,他们只关注老板的反映。这就是观察者模式。
项目整体结构是这样的:
通过一些设备,实时检测各种类型的数据,新的数据以消息的形式通过Shuttle ESB传输。
Shuttle 服务器接收到消息,它会按照需求要求,对数据进行一定的加工处理,然后将消息注册到消息管理器(注意:这里的消息管理器,就是观察者中的主题)。而每一个显示终端就是一个观察者,当消息注册到消息管理器时,显示终端就会自动更新最新消息,然后在根据要求,进行显示。
这里,大家可以看出来:Shuttle不需要关心有多少个显示终端,它只管接收数据,经过处理后,它会发送给终端。这也就是观察者的好处,将发送和接收以Pub/Sub(发布/订阅)的形式,进行解耦合。
而且,显示终端之间是相互独立的,每个显示终端只是需要接收Shuttle的消息,显示终端彼此之间相互独立。
基本结构如下图:
下面看代码实现:
每一种消息被定义为一个实体。如发送给终端的是风报警消息,那么风就定义为一种实体。这样的好处就是做到消息独立,应对变化。
风消息
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Entity
{
public class WindInfoEntity
{
public string id { get; set; }
public string name{get;set;}
}
}
雨消息
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Entity
{
public class RainInfoEntity
{
public string id { get; set; }
public string name { get; set; }
}
}
主题接口,没什么好说的。就是定义添加、移出、通知三个方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Entity;
using Observer;
namespace Subject
{
/// <summary>
/// 主题
/// </summary>
public interface ISubject
{
/// <summary>
/// 注册观察者
/// </summary>
/// <param name="o"></param>
void registerObserver(IObserver o);
/// <summary>
/// 移出观察者
/// </summary>
/// <param name="o"></param>
void removeObserver(IObserver o);
/// <summary>
/// 通知观察者
/// </summary>
/// <param name="MessageType"></param>
void notifyObservers(string MessageType);
}
}
实现了ISubject接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Subject;
using System.Collections;
using Entity;
using Observer;
namespace MessageDataSubject
{
public class MessageData : ISubject
{
public const string WindMessageType = "Wind";
public const string RainMessageType = "Rain";
//属性,用于保持数据
private ArrayList observers;
IList<WindInfoEntity> WindMessagelist = new List<WindInfoEntity>();
IList<RainInfoEntity> RainMessagelist = new List<RainInfoEntity>();
public MessageData()
{
observers = new ArrayList();
}
public void registerObserver(IObserver o) {
observers.Add(o);
}
public void removeObserver(IObserver o) {
int i = observers.IndexOf(0);
if (i > 0) { observers.Remove(o); }
}
public void notifyObservers(string pMessageType)
{
if (pMessageType == WindMessageType)
{
foreach (IObserver o in observers) {
o.Update(WindMessagelist);
}
}
if (pMessageType == RainMessageType)
{
foreach (IObserver o in observers)
{
o.Update(RainMessagelist);
}
}
}
public void measurementsChanged(string pMessageType)
{
notifyObservers(pMessageType);
}
public void setMeasurements(IList<WindInfoEntity> pWindMessagelist)
{
this.WindMessagelist = pWindMessagelist;
measurementsChanged(WindMessageType);
}
public void setMeasurements(IList<RainInfoEntity> pRainMessagelist)
{
this.RainMessagelist = pRainMessagelist;
measurementsChanged(RainMessageType);
}
}
}
定义消息公共访问的方法,因为消息管理器是系统公共访问的,所以需要定义为静态的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MessageDataSubject;
namespace CommunicationRouter
{
public class MessageManager
{
public static MessageData MessageTransfer { get; set; }
}
}
提供消息管理器的Get/Set方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MessageDataSubject;
using Subject;
using CommunicationRouter;
namespace Globle
{
public class GlobleProperty
{
public static MessageData MessageRegister
{
get { return MessageManager.MessageTransfer; }
set { MessageManager.MessageTransfer = value; }
}
}
}
观察者接口,定义观察者的消息更新方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entity;
namespace Observer
{
/// <summary>
/// 抽象观察者
/// </summary>
public interface IObserver
{
void Update(IList<WindInfoEntity> winds);
void Update(IList<RainInfoEntity> rains);
}
}
具体观察者,这里需要将当前的窗体注册到主窗体的消息管理器。然后再实现消息的更新方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Entity;
using Globle;
using MessageDataSubject;
using Observer;
using System.Windows.Threading;
namespace EquipmentObserver
{
public partial class MainWindow : Window, IObserver
{
//定义基站消息管理器
public static MessageData baseStationMessageData = new MessageData();
public MainWindow()
{
InitializeComponent();
//将当前窗体注册到主窗体的消息管理器
GlobleProperty.MessageRegister.registerObserver(this);
}
private void Window_Loaded(object sender, RoutedEventArgs e) { }
public void Update(IList<WindInfoEntity> StateWindlist)
{
if (StateWindlist == null || StateWindlist.Count <= 0)
{
return;
}
foreach (WindInfoEntity wind in StateWindlist)
{
Console.WriteLine("********风消息接收:" + new DateTime() + "====" + wind.name);
}
}
public void Update(IList<RainInfoEntity> StateRainlist)
{
foreach (RainInfoEntity rain in StateRainlist)
{
Console.WriteLine("********雨消息接收:" + new DateTime() + "====" + rain.name);
}
}
}
}
这里是主项目,一般系统登陆后,都应该进入到这里。这里,我们模拟一个发数据的模拟器。每三秒钟发送一次数据。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Entity;
using System.Timers;
using MessageDataSubject;
namespace MainClient
{
public partial class MainWindow : Window
{
//定义消息管理器
static MessageData _MessageData = new MessageData();
public MainWindow()
{
InitializeComponent();
Globle.GlobleProperty.MessageRegister = _MessageData;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//定义一个计时器
System.Timers.Timer aTimer = new System.Timers.Timer();
//到达时间的时候执行事件GetAndSendMessages
aTimer.Elapsed += new ElapsedEventHandler(callAllMessages);
aTimer.Interval = 3000; //3秒
aTimer.AutoReset = true; //设置一致执行(true)
aTimer.Enabled = true; //是否执行System.Timers.Timer.Elapsed事件
}
private void callAllMessages(object source, ElapsedEventArgs e)
{
Console.WriteLine("************"+new DateTime() +"==MainClient发送消息");
//调用风推送
getAndSendWindMessage();
//调用雨推送
getAndSendRainMessage();
}
/// <summary>
/// 获取风数据,并推送给客户端
/// </summary>
private void getAndSendWindMessage()
{
IList<WindInfoEntity> wind = new List<WindInfoEntity>();
WindInfoEntity w = new WindInfoEntity();
w.id = "w_1";
w.name = "w_风速一级";
wind.Add(w);
w.id = "w_2";
w.name = "w_风速二级";
wind.Add(w);
w.id = "w_3";
w.name = "w_风速三级";
wind.Add(w);
w.id = "w_4";
w.name = "w_风速四级";
wind.Add(w);
_MessageData.setMeasurements(wind);
}
/// <summary>
/// 获取雨数据并推送给客户端
/// </summary>
private void getAndSendRainMessage()
{
IList<RainInfoEntity> rain = new List<RainInfoEntity>();
RainInfoEntity r = new RainInfoEntity();
r.id = "r_1";
r.name = "r_风速一级";
rain.Add(r);
r.id = "r_2";
r.name = "r_风速二级";
rain.Add(r);
r.id = "r_3";
r.name = "r_风速三级";
rain.Add(r);
r.id = "r_4";
r.name = "r_风速四级";
rain.Add(r);
_MessageData.setMeasurements(rain);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
EquipmentObserver.MainWindow mainwin = new EquipmentObserver.MainWindow();
mainwin.Show();
}
}
}
这里的显示,涉及到了多线程问题,我们不做那么复杂,我们采用控制台输出(如上图)。
我们前端刷新传输数据,占用着主线程;而现在我们要在界面上将信息显示出来,所以我们还需要另一起新线程。这里我们主要是模拟项目中的观察者模式架构模型,这里就不深入阐述多线程了。(感兴趣的朋友,可以在下面留言)
这就是本文要介绍的所有内容。项目中基本的消息传输机制就是这样,项目就是利用观察者模式将发送者与接收者解耦合。
有一些比较灵活的地方。如每一个控件的位置大小,都是根据要求变化的,这时候,就是要在使用一重观察者模式,依照这种思路,再将控件附在窗体上。原理大家应该都懂得,感兴趣的朋友可以自己尝试一下。
原文:http://blog.csdn.net/liu765023051/article/details/39050799