java 适用于简单聊天应用程序的设计模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1198016/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Suitable Design Pattern for a Simple Chat Application
提问by Priyank
I am designing a simple chat application (just for the kick of it). I have been wondering of a simple design for that chat application. To give you overview.. here are the rules:
我正在设计一个简单的聊天应用程序(只是为了它的启动)。我一直想知道该聊天应用程序的简单设计。为了给你概述..这里是规则:
- Anonymous user enter chat just with a nickname. (User id) is presumably assigned by system in background.
- They can join in (subscribe to) a chat conversation. And he'll see the chat-text from other users appearing on the designated area.
- They can reply to a particular conversation and everyone else should see that.
- 匿名用户仅使用昵称即可进入聊天。(用户 ID)大概是由系统在后台分配的。
- 他们可以加入(订阅)聊天对话。他会看到来自其他用户的聊天文本出现在指定区域。
- 他们可以回复特定的对话,其他人都应该看到。
THATS IT! (See I told you it was a simple chat application). So, my intention is not really the application; but the design pattern and objects used in it.
而已!(看我告诉过你这是一个简单的聊天应用程序)。所以,我的意图并不是真正的应用程序;而是其中使用的设计模式和对象。
Now here is how I have designed it. (I am coding in java.. in case that really matters)
现在这是我设计它的方式。(我正在用 java 编码 .. 以防万一真的很重要)
- User Object - Two attributes id and nickname
- Message Object - A simple Message interface and implementation (for now) as a SimpleMessage, with a String as the attribute to contain the message.
- Chat Window Object - Basically composition of User and Messages. As it has One User Object and List of Messages.
- Chat Session - Again composition. Basically it'll have a List of Chat Windows. Each chat window registers to a chat session. Chat session is responsible to notify all chat windows when a new message appears. (Observer pattern anyone?)
- 用户对象 - 两个属性 id 和昵称
- Message Object - 一个简单的 Message 接口和实现(目前)作为 SimpleMessage,使用 String 作为包含消息的属性。
- 聊天窗口对象 - 基本上由用户和消息组成。因为它有一个用户对象和消息列表。
- 聊天会话 - 再次组成。基本上它会有一个聊天窗口列表。每个聊天窗口都注册到一个聊天会话。聊天会话负责在出现新消息时通知所有聊天窗口。(观察者模式有人吗?)
Alright.. so now I have implemented observer pattern by making ChatWindow implement "ChatListener" patter which has method called "notify(Message)". So ChatSession notifies every registered ChatWindow.
好吧..所以现在我通过让 ChatWindow 实现“ChatListener”模式来实现观察者模式,该模式具有称为“notify(Message)”的方法。所以 ChatSession 会通知每个注册的 ChatWindow。
Now here are few things I want to clarify/want your opinion on. 1. I need to have de-register method also for all chat windows, in case a chat window is closed and doesn't want to get any more notifications. That potentially means, that either I should have a "Static" central registration manager which has only one instance and then any chat window should be able to de-register itself by providing a "chat session" id. For that reason, each chat session should have an id. (Included hereon). OR I could maintain an instance of ChatSession in Chat Window too, to always have an instance ready. (I hate singletons as I think they go against oops). Another way would be to not have de-registration control of chat window, with chat window, instead the notification of window closure should come directly to ChatSession and it should do, what it should do!
现在这里有几件事我想澄清/希望你的意见。1. 我还需要为所有聊天窗口设置注销方法,以防聊天窗口关闭且不想收到更多通知。这可能意味着,要么我应该有一个只有一个实例的“静态”中央注册管理器,然后任何聊天窗口都应该能够通过提供“聊天会话”ID 来取消注册。因此,每个聊天会话都应该有一个 ID。(包括在此)。或者我也可以在聊天窗口中维护一个 ChatSession 实例,以便始终准备好一个实例。(我讨厌单身人士,因为我认为他们反对 oops)。另一种方法是没有聊天窗口的注销控制,使用聊天窗口,
Does this design makes sense at all? If you'd say it's a piece of CRAP and give me a yet better approach; you'll definitely get a BIG THANK YOU from me. Apart from observer pattern what all patterns could be used here to simplify it more or make it better. Also.. any weak points of this design, if it's appropriate but can be improved.
Also when a user types a new message in his own chat window; it needs to be propogated to all chat windows, which is what chat session does, but at the same time; does that mean that.. chat session needs to get a message with "chat window id" and message? And then it propogates it to all windows, including the one which is the owner of the message? What is a better way to handle this. I mean, a way where window lets the chat session know of the message and then chat session to all other windows. (I am thinking it'll need some if's... don't like them either)
Anyway... do let me know your comments. Also please rem. working application is not the intent, I am looking for a good discussion, good Design pattern practices and usage.
这种设计有意义吗?如果你说这是废话,并给我一个更好的方法;你一定会得到我的感谢。除了观察者模式之外,所有模式都可以在这里使用以进一步简化或使其更好。另外..这个设计的任何弱点,如果合适但可以改进。
此外,当用户在他自己的聊天窗口中键入新消息时;它需要传播到所有聊天窗口,这是聊天会话所做的,但同时;这是否意味着.. 聊天会话需要获取带有“聊天窗口 ID”和消息的消息?然后它将它传播到所有窗口,包括消息的所有者?有什么更好的方法来处理这个问题。我的意思是,窗口让聊天会话知道消息,然后与所有其他窗口聊天会话的方式。(我想它需要一些如果......也不喜欢它们)
无论如何...请让我知道您的意见。也请rem。工作应用程序不是意图,我正在寻找一个很好的讨论,好的设计模式实践和用法。
Complete code below, if it gives you a high... Feel free to tear it apart and bring up issues related to almost any semantics.
下面的完整代码,如果它给你一个高...请随意拆开它并提出与几乎所有语义相关的问题。
package com.oo.chat;
public class User {
private Long userId;
private String nickname;
public User(Long userId, String nickname) {
this.userId = userId;
this.nickname = nickname;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Long getUserId() {
return userId;
}
public String getNickname() {
return nickname;
}
public boolean equals(Object objectToCompare) {
if (!(objectToCompare instanceof User)) {
return false;
}
User incoming = (User) objectToCompare;
if (incoming.getNickname() != null && incoming.getUserId() != null) {
if (incoming.getNickname().equalsIgnoreCase(this.nickname)
&& incoming.getUserId().equals(this.userId))
return true;
}
return false;
}
}
package com.oo.chat;
public interface Message {
public String getValue();
public void setValue(String value);
}
package com.oo.chat;
public class SimpleMessage implements Message {
private String value;
public SimpleMessage() {
}
public SimpleMessage(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
package com.oo.chat;
public interface ChatListener {
public void notify(Message newMessage);
}
package com.oo.chat;
import java.util.ArrayList;
import java.util.List;
public class ChatWindow implements ChatListener {
private User user;
private List<Message> messageList;
private Long id;
public User getUser() {
return user;
}
public List<Message> getMessageList() {
return messageList;
}
public void setUser(User user) {
this.user = user;
}
public void setMessageList(List<Message> messageList) {
this.messageList = messageList;
}
public void addMessageToList(Message newMessage) {
if (this.messageList == null) {
this.messageList = new ArrayList<Message>();
}
this.messageList.add(newMessage);
}
public void notify(Message newMessage) {
addMessageToList(newMessage);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
package com.oo.chat;
import java.util.ArrayList;
import java.util.List;
public class ChatSession {
private List<ChatListener> registeredChatListeners;
public void register(ChatWindow chatWindow) {
if (registeredChatListeners == null)
registeredChatListeners = new ArrayList<ChatListener>();
registeredChatListeners.add(chatWindow);
}
public List<ChatListener> getRegisteredChatListeners() {
return registeredChatListeners;
}
public void setRegisteredChatWindows(
List<ChatListener> registeredChatListeners) {
this.registeredChatListeners = registeredChatListeners;
}
public void incomingMessage(Long chatListenerId, Message message) {
publish(message);
}
protected void publish(Message messageToPublish) {
if (registeredChatListeners != null) {
for (ChatListener eachListener : registeredChatListeners) {
eachListener.notify(messageToPublish);
}
}
}
}
Thanks to all contributers in advance. Cheers
提前感谢所有贡献者。干杯
采纳答案by Brian Agnew
Just looking at the Userobject, why does equality depend on id andnickname ? That seems a little counter intuitive to me. I would expect if you have an id, then that is the identification of the object, and hence what you'd use in an equality condition.
只看User对象,为什么相等取决于 id和nickname ?这对我来说似乎有点违反直觉。我希望如果你有一个 id,那么这就是对象的标识,因此你会在相等条件下使用什么。
I see also that you have a setter for the user id. So do you really want to change user id ? I see that you can change the nickname, which makes sense. But I would expect the id to remain constant.
我还看到您有一个用于用户 ID 的设置器。那么您真的要更改用户 ID 吗?我看到你可以更改昵称,这是有道理的。但我希望 id 保持不变。
Note also that since you're overriding equals(), you should also override hashCode().
另请注意,由于您要覆盖 equals(),因此您还应该覆盖 hashCode()。
Now, if hashCode() and equals() rely on immutable fields (e.g. the id), then the results of hashCode() won't change, and if you put a User in a hashed collection (e.g. HashMap) then you're not going to lose it later (which is very confusing)!
现在,如果 hashCode() 和 equals() 依赖于不可变字段(例如 id),那么 hashCode() 的结果不会改变,如果您将 User 放入散列集合(例如 HashMap)中,那么您就是以后不会丢失它(这很令人困惑)!
Finally (!) I would protect the constructor and setters against null nicknames (make them throw IllegalArgumentExceptions) and then code like equals() doesn't have to worry about a null nickname (unless 'null' has a meaning for the nickname). I would do the same for id, since you have this as a Long (object). But couldn't this be a primitive long ?
最后 (!) 我会保护构造函数和 setter 免受空昵称的影响(让它们抛出 IllegalArgumentExceptions),然后像 equals() 这样的代码不必担心空昵称(除非 'null' 对昵称有意义)。我会对 id 做同样的事情,因为你把它作为一个 Long(对象)。但这不能是一个原始的 long 吗?
回答by jsight
The basic design looks sound to me. Obviously to complete this, you would nee to add a lot more features. The current design keeps all messages in memory indefinitely, but at some point you are going to need code for purging old messages.
基本设计在我看来很合理。显然,要完成此操作,您需要添加更多功能。当前的设计将所有消息无限期地保留在内存中,但在某些时候您将需要用于清除旧消息的代码。
The few significant design issues that I do see are:
我确实看到的几个重要的设计问题是:
- The message interface doesn't link back the the sender of the message - Most chats show who said what, and this will be difficult without a User field in the message.
- The message interface doesn't have a time property. This will make purging old messages more difficult.
- 消息界面不会链接回消息的发件人 - 大多数聊天显示谁说了什么,如果消息中没有用户字段,这将很困难。
- 消息接口没有时间属性。这将使清除旧邮件更加困难。
回答by Gregory Mostizky
I would suggest looking into messaging frameworks instead of using Observer pattern.
我建议研究消息传递框架而不是使用观察者模式。
Take a look at this simple implementation which will be sufficient for your toy project - eventbus(no longer available). Alternatively you could go with full blown JMS implementation like ActiveMQ.
看看这个简单的实现,它足以满足您的玩具项目 - eventbus(不再可用)。或者,您可以使用完整的 JMS 实现,如ActiveMQ。
Basically it allows you to define a common bus to which you can register and unregister participants, and also send messages that all of the participants will see. The big advantage over observer pattern is the very low coupling between the participants - you don't register with each object to get his messages - you just register once with the bus. In addition you get asynchronous processing - let's say you have 1000 chat sessions - if you use observer it means that for each message to complete it will take to update 1000 sessions. With message framework message send is very quick, and notifying all 1000 sessions is done in the background.
基本上它允许您定义一个公共总线,您可以在其中注册和取消注册参与者,并且还可以发送所有参与者都会看到的消息。与观察者模式相比,最大的优势在于参与者之间的耦合非常低——您无需向每个对象注册来获取他的消息——您只需在总线上注册一次。此外,您将获得异步处理 - 假设您有 1000 个聊天会话 - 如果您使用观察者,则意味着要完成每条消息,它将需要更新 1000 个会话。使用消息框架消息发送非常快,并且通知所有 1000 个会话是在后台完成的。

