【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:9.观察者模式
前言
在一个程序的迭代过程中,复杂度渐渐上升,可能会出现一些跨模块的调用的需求,若是直接得到引用来进行使用,会导致模块间的耦合度越来越高,消息机制是一种用于解耦这种耦合度高模块的好方法,它的使用非常灵活便利,可以解决一对多(一个发送者,多个接受者)和需求频繁的变更,甚至于,在添加新的需求时可以完全的不改动旧的代码。在Java中消息机制可以用观察者模式来实现。
前缘未了
在【迷你微信】基于MINA、Hibernatye、Spring、Protobuf的即时聊天系统:7.项目介绍之架构中,我们说道了在发送聊天消息时,可能出现接受者不在线的情况,因为可能出现的各种复杂情况,封装到我们将发送消息这个行为ServerModel_Chatting类中。
不知道大家有没有注意到,微信的聊天消息是可能发送失败的,也许因为网络问题,也许因为对方根本没有开启微信,若是像登陆、注册等请求,服务器向客户端发包失败,想偷懒的话,是可以不作处理的,不仅因为这种事件发生的几率较少,也是因为这种异常的抛弃不会造成不可接受的后果,大不了再点击一次登陆咯。但是,请想象一下,若是您的好友给您发了一条消息,因为您的一时网络问题,服务器不能成功的发送给您,就将其直接抛弃,那么……您可能被您的友人认为是无视了他的消息,这种后果是不可接受的。
所以,我们要对发送失败的消息进行存储,并在对方上线后及时的推送过去,这个模块是比较复杂的,而且牵扯到复用(单聊、群聊)问题,还有存储为永久数据问题,所以我们将其独立成单独的一个类来处理。在ServerModel_Chatting类的sendChatting方法中我们首先要从ServerModel中查找接受者是否在线,在线则可以直接推送;而不在线的情况下,则需要暂时存储下来,等待接受者上线。
// 发送一条聊天消息
public void sendChatting(final Chatting chattings) {
ClientUser clientUser = serverModel.getClientUserByUserId(chattings.getReceiverUserId());
// 如果不在线,则暂存
if (clientUser == null){
addChatting(chattings);
return;
}
// 发送
}
// 暂存一条消息
public void addChatting(Chatting chatting) {
// 保存消息
}
可以看到,发送聊天消息这个过程中,首先会判断接受者是否在线,若是不在线会将消息暂存,在接受者上线后将接收到服务器推送的消息。然而,这个模块如何知道接受者上线了呢?我们通过观察者模式,在用户登陆时通知本模块进行查询是否有为接收消息。
观察者模式
观察者模式中,首先要有被观察者和观察者两个角色(可以有多个观察者,俗称”围观“(⊙o⊙)…),被观察者要继承Observable类,观察者要对被观察者对象调用addObserver 方法。
此处代码皆为删减版,欲查看完整代码,请参考开源项目《迷你微信》服务器
先说说我们如何在用户登陆时通知消息发送模块进行查验。
1.在Server_User的 login方法中,用户登陆成功时,会调用ServerModel的 clientUserLogin 方法,
public void clientUserLogin(ClientUser clientUser, String userId) {
// 保存用户信息
clientUser.onLine = true;
clientUser.userId = userId;
clientUserIdTable.put(userId, clientUser);
// 通知变更
setChange();
notifyObservers(new ObserverMessage_Login(clientUser.ioSession, userId));
}
在上面的方法中,我们将自己设置为变更状态,然后通知所有观察者。
2.观察者ServerModel_Chatting添加监听并进行处理
serverModel.addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
ObserverMessage om = (ObserverMessage) arg;
if (om.type == ObserverMessage.Type.Login) {
// 发送登陆者未接收的消息
}
}
}
在这里,我们对serverModel的实例化对象添加了一个监听,判断是否为监听事件,若是,则发送未接收的消息。
后话
其实在本项目中,使用观察者模式并不是一个好的做法,理由如下:
- 被监听者需要继承Observable基类,在Java这种单继承的语言中,这是不友好的。
- 观察者观察到的消息需要每次都进行类型的判断,针对性较差。
- 观察者需要得到被观察者的引用才能添加观察,耦合度没有太大的减小。
因为帖主的这个项目中,目前只有这一个点需要用到这样一种功能,所以偷了个懒。一旦需求复杂度提升,我们需要将观察者模式做一些变化,将其做成完整的消息机制,使其具备一下几个特点:
- 消息发送者不需要继承基类
- 可以根据需要编写不同的消息,接收者只需要注册监听指定的消息类型就可以接收到消息特定消息,而不是所有消息都接收。
- 消息接收者不需要使用发送者的引用。
帖主的想法如下:
- 首先,由使用者编写各种消息MessageA, MessageB。
- 有一个消息管理器MessageManager,其中拥有一个Hashtable<String, LinkedList>的哈希表对象,第一个参数是消息的类名(如MessageA),第二个链表参数是注册监听的所有监听者。
- 监听者实现监听接口,并将自己的引用和要注册监听的消息类型传入MessageManager进行监听。
- 当消息被发送到MessageManager时,MessageManager会从Hashtable中取出注册监听的监听者队列,依次发送消息。
【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:9.观察者模式的更多相关文章
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:0.概述
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 序言 帖主和队友仿制了一个简单版的微信,其中,队友是用Unity3D做前段,帖主用Java的Mina.Hiberna ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:7.项目介绍之架构(1)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 前言 <迷你微信>服务器端是使用Java语言,Mina框架编写的,一个良好的架构关系到后期迭代的方便程度 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:8.自定义传输协议
欢迎阅读我的开源项目<迷你微信>服务器)与<迷你微信>客户端 前言 在上一篇中,我们讲到了<迷你微信>服务器)的主体架构,还讲到了如何在现有功能上进行拓展,但是拓展 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:6.技术简介之Protobuf
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 protocolbuffer(以下简称Protobuf)是google 的一种数据交换的格式,它独立于语言,独立于平 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:5.技术简介之Hibernate
目录 序言 配置 hibernate.cfg.xml配置文件 加载hibernate.cfg.html配置文件并获取Session 对象的注解配置 增删改查 具体的增删改查代码 数据库操作的封装 连接 ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统 :1.技术简介之Mina连接
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 Apache MINA(Multipurpose Infrastructure for Network Applic ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:4.技术简介之Spring
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 Spring是一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:3.技术简介之MinaFilter——LoggingFilter (转)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 LoggingFilter 接下来,使我们对Filter介绍的最后一个——LoggingFilter. 与Proto ...
- 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:10.项目介绍之架构(2)
欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 前言 前面我们讲到<迷你微信>服务器端的主架构,现在我们来描述一下它的模块详细信息. 网络模块 从上图我 ...
随机推荐
- <c和指针>学习笔记5动态内存分配和预处理器
1 动态内存 比如声明数组得时候,我们需要提前预估数组长度,分配大了浪费,少了就更不好操作了.从而引入动态分配,需要的时候再分配. (1)malloc和free void *malloc(size_t ...
- java之格式化输出
参考http://how2j.cn/k/number-string/number-string-foramt/320.html#nowhere 格式化输出 如果不使用格式化输出,就需要进行字符串连接, ...
- cookie 、Session 和自定义分页
cookie cookie的由来 大家都知道Http协议是无状态的. 无状态的意思 是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系, 他不会受前面的请求响应情况直接影响, ...
- 关于goneaway及499
关于上面现象的分析如下 问题描述: 接口偶发性出现接口耗时过长的情况 根源: “sockets的快速回收”机制被启动 简单代码+数据分析: 1. 经简单分析,耗时主要出现在连接数据库的方法: ...
- centos运行netcore error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord.
Error: Another program is already listening on a port that one of our HTTP servers is configured to ...
- C#面向对象三大特性之三:多态
比起前面的封装和继承,多态这个概念不是那么好理解.我们还是从一个事例开始: 公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat).狗(Dog).羊(Sheep),这些动物都有共同的特性, ...
- 浅谈JavaScript--函数重载
个人认为重载就是一组具有相同名字.不同参数列表的函数(方法). 从语言角度来说,javascript不支持函数重载,不能够定义同样的函数然后通过编译器去根据不同的参数执行不同的函数. 但是javasc ...
- cogs 2123. [HZOI 2015] Glass Beads
2123. [HZOI 2015] Glass Beads ★★★ 输入文件:MinRepresentations.in 输出文件:MinRepresentations.out 简单对比时 ...
- 2017-10-24 NOIP模拟赛
括号序列 (bracket) Time Limit:1000ms Memory Limit:128MB 题目描述 LYK有一个括号序列,但这个序列不一定合法. 一个合法的括号序列如下: ()是合法 ...
- 解读人:刘杰,Targeted Quantitative Kinome Analysis Identifies PRPS2 as a Promoter for Colorectal Cancer Metastasis(PRM-磷酸化激酶定量发现结肠癌转移促进因子-PRPS2)
关键词:PRM,kinase,colorectal cancer, metastasis, PRPS2 来自加州大学河滨分校的Yinsheng Wang教授应用PRM技术筛选出介导结肠癌细胞转移促进因 ...