模拟项目结构——观察者模式

观察者模式又叫做发布——订阅(Publish/Subscribe)模式。它的概念在我之前的博文中,也多次介绍过。今天,通过一个小Demo,模拟一下项目中使用观察者模式的基本结构。

概念回顾

首先,回顾一下观察者模式的概念。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象。这个主体对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

对于这些概念的东西,我们要结合实例来理解,这里我们联想大话设计模式中的例子:

公司中有看球的观察者A,有炒股票的观察者B;而老板和秘书两个通知者,就是两个主题。观察者A和观察者B同时监视着老板的反映。一旦老板有了新的指示,观察者A和观察者B以及所有的其他观察者,都会受到通知。

老板不关心具体谁会收到通知,他只管发通知;而且每个观察者之间也相对独立,他们只关注老板的反映。这就是观察者模式。

项目结构模拟

项目整体结构是这样的:

通过一些设备,实时检测各种类型的数据,新的数据以消息的形式通过Shuttle ESB传输。

Shuttle 服务器接收到消息,它会按照需求要求,对数据进行一定的加工处理,然后将消息注册到消息管理器(注意:这里的消息管理器,就是观察者中的主题)。而每一个显示终端就是一个观察者,当消息注册到消息管理器时,显示终端就会自动更新最新消息,然后在根据要求,进行显示。

这里,大家可以看出来:Shuttle不需要关心有多少个显示终端,它只管接收数据,经过处理后,它会发送给终端。这也就是观察者的好处,将发送和接收以Pub/Sub(发布/订阅)的形式,进行解耦合。

而且,显示终端之间是相互独立的,每个显示终端只是需要接收Shuttle的消息,显示终端彼此之间相互独立。

基本结构如下图:

代码实现

下面看代码实现:

1、Entity消息

每一种消息被定义为一个实体。如发送给终端的是风报警消息,那么风就定义为一种实体。这样的好处就是做到消息独立,应对变化。

风消息

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; }
    }
}

2、Subject接口

主题接口,没什么好说的。就是定义添加、移出、通知三个方法。

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);
    }
}

3、主题的实现MessageDataSubject

实现了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);
        }
    }
}

4、CommunicationRouter

定义消息公共访问的方法,因为消息管理器是系统公共访问的,所以需要定义为静态的。

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; }
    }
}

5、Globle

提供消息管理器的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; }
        }
    }
}

6、Observer接口

观察者接口,定义观察者的消息更新方法。

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);
    }
}

7、EquipmentObserver观察者

具体观察者,这里需要将当前的窗体注册到主窗体的消息管理器。然后再实现消息的更新方法。

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);
            }
        }
    }
}

8、客户端

这里是主项目,一般系统登陆后,都应该进入到这里。这里,我们模拟一个发数据的模拟器。每三秒钟发送一次数据。

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();
        }
    }
}

9、结果显示

这里的显示,涉及到了多线程问题,我们不做那么复杂,我们采用控制台输出(如上图)。

我们前端刷新传输数据,占用着主线程;而现在我们要在界面上将信息显示出来,所以我们还需要另一起新线程。这里我们主要是模拟项目中的观察者模式架构模型,这里就不深入阐述多线程了。(感兴趣的朋友,可以在下面留言)

这就是本文要介绍的所有内容。项目中基本的消息传输机制就是这样,项目就是利用观察者模式将发送者与接收者解耦合。

有一些比较灵活的地方。如每一个控件的位置大小,都是根据要求变化的,这时候,就是要在使用一重观察者模式,依照这种思路,再将控件附在窗体上。原理大家应该都懂得,感兴趣的朋友可以自己尝试一下。

时间: 2024-10-10 20:59:32

模拟项目结构——观察者模式的相关文章

云笔记开发记录一:node-webkit 项目结构?

node-webkit 项目结构? 一般用过nodejs写过点小东西的人,都应该知道nodejs项目一般都有一个package.json文件,这个package.json文件,该文件定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称.版本.许可证等元数据). 为什么要提 nodejs 的 package.json文件 呢,因为, node-webkit 项目它也有自己的 package.json文件 . 但是这两者是不同的! node-webkit的package.json是用来配置n

java创建运行以及项目结构

一 创建java project 再src下添加class,选择一个class添加main方法作为程序的入口 二.项目结构: src下添加不同的包,命名方法为com.jikexueyuan.hello.main/model/view.此时硬盘中会有项目名称文件夹下有bin和src,全部对应包的一级级目录,src中存放java文件,bin存放class二进制文件.此时还可以创建lib文件夹,用于存放第三方的库.将库文件add to path即可被引用. 三.eclipse代码自动补全 Window

ThinkPHP - 配置项目结构

配置项目结构: 项目如果分为前后台使用. 那么最关键的就是,使用公共部分文件的划分,其中最为核心的就是公共配置文件的使用. 下面介绍的就是怎么将前后台项目的公共部分提起出来. 首先是其他公共的文件夹: 这是最顶层的文件配置. 详细的目录说明,看下面: E:\PHP\WWW\THINKPHP │ admin.php //后台主入口文件 │ index.php //前台主入口文件 ├─Admin //后台文件夹 │ ├─Common │ ├─Conf //后台配置文件夹 │ │ config.php

上门洗车APP --- Androidclient开发 之 项目结构介绍

上门洗车APP --- Androidclient开发 之 项目结构介绍 前言 尽管公司项目较紧,但还是抽空给大家继续更新. o_O"~ 欢迎大家的关注,非常高兴和大家共同学习.前面给大家分享了项目中的以下内容: 上门洗车APP --- Androidclient开发 前言及业务简介 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(一) 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二) 之前有非常多朋友私信过来说想打包一份源代码学习

[Erlang危机](2.1)项目结构

?? 原创文章,转载请注明出处:服务器非业余研究http://blog.csdn.net/erlib 作者Sunface Project Structure The structures of OTP applications and of OTP releases are different. An OTP application can be expected to have one top-level supervisor (if any) and possibly a bunch of

CSLA的项目结构(一)

由于我也是边看边学,在很多概念不是很清晰的情况下,也不好将书中的大段内容全部摘抄过来,所以结合项目源码先分析再总结,就成目前比较可行方案,第一篇先从项目结构入手. 项目源码下载后,主要的功能集中在Core项目文件夹的CSLA项目中,其他项目或从此处继承,或引用此项目,因此对整个项目的分析从此开始. 需要说明的是:解决方案\Core文件夹下,CSLA与CSLA.Net4的类基本是相同的,只是针对的编译环境不同,其他类似的文件夹也是这样的情况. 因为找到了主要的项目,其他在我的机子上不能编译或我用不

maven(三):maven项目结构及其运行机制

在上一篇中讲了如何创建maven项目,现在回到那个项目 项目结构 src/main/java:java代码目录 src/main/resources:资源目录,比如spring.xml文件,properties参数等 java和resources目录的内容都会编译到classpath下,也就是和传统项目的src目录一样的作用,这里分成多个目录是为了开发时方便管理资源 libraries:默认只有jre和maven,没有引入tomcat库,我们的项目应该可以在所有的web服务器中运行,而不仅仅是t

项目结构

新建项目 新建工程,类型为Web Project,设置默认编码为UTF-8,并创建如下文件夹: Source Folder  –src : 项目源码  –config : 配置文件  –test : 单元测试 普通文件夹  –WebRoot/style css与图片等文件  –WebRoot/script js脚本文件  –WebRoot/WEB-INF/jsp jsp页面文件 包结构 a. 实体    com.shizongger.oa.domain b. Dao    com.shizong

Android实际开发中的首页框架搭建(一、项目结构搭建)

前段时间忙得不可开交,一直想抽个时间写一个博客,然后就一直拖到了现在,确实感觉有点愧疚... 这段时间买了几本书正在看,想让自己好好沉下心来,又去慕课网看了些视频,确实发现以前自己落下了蛮多知识点,还是有点收获, 所以,在此呼吁一下,干我们这行,需要不断学习,只有在学习中,才能明白自己有多水,才能让自己不断变强! 好了,进入正题,这一次准备了一些很基础的东西,但也是非常重要的东西,对于我们实际开发真的很有帮助 知识点一:使用BaseFragment/BaseActivity的作用:抽象到父类的思