知识大全 开发设计模式——asp.net中实现观察者模式

Posted

篇首语:幼敏悟过人,读书辄成诵。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 开发设计模式——asp.net中实现观察者模式相关的知识,希望对你有一定的参考价值。

    在中实现观察者模式?难道中的观察者模式有什么特别么?嗯 基于Http协议的Application难免有些健忘 我是这样实现的 不知道有没有更好的办法?

  先谈谈需求吧 以免陷入空谈

  最近一个Case 这样的需求 很多客户端不断的向Web Application提交数据 管理员进入Web的管理页面可以即时的看到这些数据 有多个管理员可以同时浏览 且管理员浏览的数据从管理员开始监视那个时刻起 不能显示以前的数据 从这个场景一看 明显的观察者模式 管理员开始监视时 订阅数据 数据到达的时候向所有订阅了数据的管理员广播数据

  需求如下图

  

  

  有了发布者还需要订阅者 我们实现管理员类 来订阅数据

    public class Admin        /**//// <summary>     /// 用这个保存所有收到的数据     /// </summary>     public IList<string> MessageList     get; set;      public Admin(Monitor monitor)              MessageList = new List<string>();         monitor DataIn += new EventHandler< DataEventArgs>(ReciveMessage);     [img]_ / / gif[/img]     private void ReciveMessage(object sender DataEventArgs e)[img]_ / / gif[/img]         MessageList Add(e Message);[img]_ / / gif[/img]     [img]_ / / gif[/img]   [img]_ / / gif[/img]

  Ok 需要具备的元素我们都写好了 但是如何让它们工作起来?如果使Winform程序 那将毫无悬念

  分析 我们碰到的问题

  第一个问题 当客户端发送一个数据包 我们是实例化一个新的Monitor么?如果是 哪么每次实例化一个全新的Monitor 所有在它上面订阅的事件将全部消失了 如果不是那这个Monitor将如何存在呢?总不能真空吧 两个请求之间如何保存数据呢?不过再把需求一读 好像整个应用程序中就只需要也只能有一个这样的Monitor呢 该是单件模式上场的时候了

  在上面的Monitor的实现中添加下面的代码

    [img]_ / / gif[/img]private static Monitor _instance = null;[img]_ / / gif[/img]public static Monitor Current[img]_ / / gif[/img]   get [img]_ / / gif[/img]   [img]_ / / gif[/img]     if (_instance == null)[img]_ / / gif[/img]       _instance = new Monitor();[img]_ / / gif[/img]     return _instance;[img]_ / / gif[/img]   [img]_ / / gif[/img]

  但是本系统存在多个客户端 所以为了避免多线程造成问题 还是来Double Check一下吧 修改上面的代码如下

    [img]_ / / gif[/img]public static Monitor Current[img]_ / / gif[/img]     [img]_ / / gif[/img]         get [img]_ / / gif[/img]         [img]_ / / gif[/img]           object o = new object();[img]_ / / gif[/img]           if (_instance == null)[img]_ / / gif[/img]           [img]_ / / gif[/img]             lock (o)[img]_ / / gif[/img]             [img]_ / / gif[/img]                 if (_instance == null)[img]_ / / gif[/img]                   _instance = new Monitor();[img]_ / / gif[/img]             [img]_ / / gif[/img]           [img]_ / / gif[/img]           return _instance;[img]_ / / gif[/img]         [img]_ / / gif[/img]     [img]_ / / gif[/img]

  (PS:为什么使用单件就可以跨请求保存实例了呢?因为这里使用了一个static member保存Monitor的引用 static 的GC里面是被作为Root的 详细内容请参见框架程序设计那本书)

  第二个问题 当管理员页面的ajax请求的时候 每两个请求如何保存数据?呵呵 上面那个问题不是说了么 用单件 但是单件是全局存在的 我们的管理员是多个 每个管理员可以决定是否订阅数据 以及什么时候订阅 想起来没?除了全局数据外我们还有Session

  在管理页面上我放置一个 开始监视 的按钮 这个按钮使用ajax请求服务器端的一个HttpHandler 在Handler的ProcessRequest方法里这样来做

    [img]_ / / gif[/img]Admin admin = context Session[ monitor_listener ] as Admin;[img]_ / / gif[/img]if(admin == null)[img]_ / / gif[/img]   admin = new Admin(Monitor Current);[img]_ / / gif[/img]   context Session[ monitor_listener ] = admin;[img]_ / / gif[/img][img]_ / / gif[/img]

  注意 由于这个Handler需要访问Session 所以你需要让这个Handler继承IRequiresSessionState接口(为什么使用继承而不用实现这个术语?实际上这个接口是一个标记接口 没有任何需要实现的成员 只是标记这个Handler可以访问Session 我不知道为什么MS不使用Attribute 是不是更合理些)

  在管理页面还有个一个SetInterval不断的调用一个含有ajax的方法 去请求另外一个Handler 这个Handler将Admin收到的数据返回到web页面 让我们来看看这个Handler的部分实现

    [img]_ / / gif[/img]public void ProcessRequest(HttpContext context)[img]_ / / gif[/img]     context Response Buffer = true;[img]_ / / gif[/img]     context Response ExpiresAbsolute = System DateTime Now AddSeconds( );[img]_ / / gif[/img]   context Response Expires = ;[img]_ / / gif[/img]   context Response CacheControl = no cache ;[img]_ / / gif[/img]   Admin admin = context Session[ monitor_listener ] as Admin;[img]_ / / gif[/img]   if (admin == null || admin MessageCollection == null || admin MessageCollection Count <= )[img]_ / / gif[/img]return;[img]_ / / gif[/img]   string[] messages = new string[admin MessageCollection Count];[img]_ / / gif[/img]   admin MessageCollection CopyTo(messages );[img]_ / / gif[/img]   StringBuilder sb = new StringBuilder();[img]_ / / gif[/img]   for (int i = ; i < messages Length; i++)[img]_ / / gif[/img]   [img]_ / / gif[/img]     sb AppendFormat( <li> </li> messages);[img]_ / / gif[/img]   [img]_ / / gif[/img]   admin MessageCollection Clear();[img]_ / / gif[/img]   context Session[ monitor_listener ] = admin;[img]_ / / gif[/img]   context Response Write(sb);[img]_ / / gif[/img]   context Response Flush();[img]_ / / gif[/img][img]_ / / gif[/img]

  OK 一个在环境中实现的观察者模式基本上就算完成了 不过上面只有怎样订阅 那什么时候取消订阅了 可以在Session_End事件里面取消订阅

  还查看了一些关于长连接的文章 发现这个不错 准备改进一下

  完整的代码稍后提供 希望这块转头能引来一些玉

  写完这个Post后本来想把完整代码实现传上来 后来看到不少园友提出异议 看了大家的留言后我也一直在思索 我为什么这样做?当初我是怎样想到这个解决方案的?我在几个解决方案之间做了取舍了么?我这样做是不是矫枉过正了?经过这些思考有了现在的这个Post

  首先我进一步谈一下需求

  这是一个Web Application 有很多客户端向服务器端提交数据(客户端是C++的 以 post方式向服务器端提交二进制数据 服务器端解析这个二进制包 数据提交很频繁) 管理员可以进入监视页面浏览这些数据 数据要即时的 客户端发来一条 管理员屏幕上要马上可以看到 允许多个管理员同时监视即时数据 所有管理员看到的数据都是一样的(目前是这样的 也许以后对管理员要分角色 各角色管理员看到的信息将不同)

  由于数据提交非常频繁 客户要求不允许频繁的数据库操作 所以我将数据保存在一个IList的缓存里面 当这个IList的大小超过了我在配置文件里定义的大小的时候就将数据批量插入到数据库

  下面我将以我当初思考的思路为主线描述

  第一个版本

    //在程序里我写了一个静态类 这个静态类保存整个程序中共享的一些数据 相当于原来的//Application对象 但是静态成员是编译期类型检查的public static ApplicationData  //这个队列用来保存客户端传递过来的数据 当队列达到一定长度的时候同步到数据库  public static Queue<DataHead> OperateDataList = new Queue<DataHead>();//这个List也是保存客户端传递过来的数据的 但它是为监视准备数据的 //当一个监视页面的请求到来的时候将这个List的数据Response过去 然后Clear这个//List  public static IList<DataHead> MonitorDataList = new List<DataHead>();public class ReciveDataHandler : IHttpHandler  //……  Public void ProcessRequest(HttpContext context)      //解析从客户端传递过来的数据    DataHead data = GetData(context);    OperateDataList Add(data);    If(OperateDataList Count > BufferSize)            //将数据写入到数据库        AddToBase();    MonitorDataList Add(data);//监视页面从这里获取数据public class MonitorHandler : IHttpHandler  //……  Public void ProcessRequest(HttpContext context)      If(MonitorDataList Count > )            //将MonitorDataList里的数据Response出去        OutPut();        MonitorDataList Clear();

  说实话 我当初做出这个的时候觉得一点问题都没有 开始的时候客户测试也没有发现任何问题 终于有一天客户和我同时测试部署在同一IIS的时候 问题出现了 只有一个监视页面有数据 看到这个后我还百思不得其解 顺着程序的执行流程一步一步走下去 没有找出任何错误 后来做了下日志 原来MonitorDataList是一个全局共享的 一个在监视把数据Clear了后别人就无法获取数据了 不知道有没有人这样做过 有时候忘记了自己正在做一个web程序 而web程序是一个并发的 对一些共享资源的访问有着微妙的问题 如果没有记住这点 按照程序流程的执行步骤是找不出任何问题的

  怎么办?再一看这不是事件订阅所描述的场景么?所以就有了上一篇Post的Solution 不过那个方案受到不少人质疑 其中金色海洋提出这样的方法

    Public class ReciveData : IHttpHandler  //………   //将客户端传递过来的数据存入数据库Public class MonitorHandler : IHttpHandler  //………  //为null的时候说明该管理员第一次监视  If(Session[ id ] == null)      //根据时间从服务器取出数据    //并将取出数据的最后一个id保存在session中    Session[ id ] = id;//不为null则说明该管理员已经开始监视了Else  //根据session里保存的最后一个id 取出大于那个id的数据  Session[ id ] = currentId;

  看似这个方案不错 我尝试着将我的程序修改为这样 但是我将上面的代码编写完 我发现我不可以再进行下去了 上面的方案满足不了我的需求 客户明确要求了客户端提交的数据要先缓存然后缓存超过配置大小(这个大小还需要可以在配置文件里面配置 以便可以经过测试找出一个最合理的值) 而这种Session记录的方案是依靠数据库来保存数据 这个Session[ id ]就相当于一个游标 这个游标指向的是数据库 那好 我们将Session[ id ]指向缓存数据 但是请注意缓存随时可能超过设置大小而被同步到数据库并被清空

  经过一番思考后我还是回到我自己的Solution上 不过我又有了新的看法了 不是要将数据先缓存么?看看这个缓存 实际上她也是个观察者 至于她执行怎样的缓存策略是她的事情 如是我又有了一个新类

cha138/Article/program/net/201311/13325

相关参考

知识大全 ASP.NET会话(Session)模式

ASP.NET会话(Session)模式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

知识大全 ASP.NET中的session存储模式运用

ASP.NET中的session存储模式运用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在中

知识大全 ASP.NET企业服务总线实现模式

ASP.NET企业服务总线实现模式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!ESB在企业体系结

知识大全 观察者模式 - 设计模式

  观察者模式定义了一个一对多的对象关系一个主体对象对应多个观察者对象当主体对象发生改变时所有它对应的观察者对象都会自动得到通知并更新  本文将给出一个相应的事例具体说明观察者模式是如果工作的这个事例

知识大全 分享关于ASP.NET中等安全模式的一些经验

分享关于ASP.NET中等安全模式的一些经验  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  如果

知识大全 设计模式-快餐简解-观察者模式

  【观察者模式应用场景举例】  比如在玩极品飞车这款游戏每一个车手到达终点的时候都会在其它车手的屏幕上显示某某人以多少时间到达终点的提示其实这就是一个典型的观察者模式的应用观察者模式即是典型的双向一

知识大全 java 设计模式之 观察者模式(Observer)

java设计模式之观察者模式(Observer)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 ASP.NET谈三层结构与MVC模式的区别

ASP.NET谈三层结构与MVC模式的区别  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在CS

知识大全 php设计模式之观察者模式的应用详解

 代码如下:<?php//抽象主题interfaceSubjectpublicfunctionattach($observer);publicfunctiondetach($obser

知识大全 .net设计模式之观察者模式

  故事    小雪是一个非常漂亮的女孩漂亮的女孩总是有很多的追求者而且追求者的队伍在不断的变动随时有人进入这个队伍也有人退出男孩们追求女孩时总是表现出%的关心当小雪私自游玩时总是不断收到追求者询问小