本文将基于上一篇用Java实现事先驱动框架(一)来模拟实现一个简单的聊天室系统
结构设计
首先确定简单聊天系统的基本对象。系统需要两种基本数据结构:用户与聊天室。
用户类
用户的定义如下。每一个进行聊天的用户都有一个名字。
private static class User {
public String name;
public User(String name) {
this.name = name;
}
}
聊天室类
聊天室需要维护当前在聊天室中的用户,其结构如下。
private static class ChatState {
private ArrayList users;
public ChatState() {}
}
行为
首先思考一下,一个聊天室程序包含的基本事件。
用户到来:当用户进入聊天时产生该事件。
用户离开:当用户离开聊天时产生该事件。
用户发消息:当用户在聊天时发送消息时产生该事件。
聊天室与用户应能够响应并处理与自身相关的事件。下面将详细描述具体实现。
聊天室的行为状态
聊天室应能够处理与其相关的事件。因此需要能够支持下列操作。
添加用户到聊天室
将用户从聊天室删除
广播消息给所有聊天室中的用户
广播消息的接受者是所有的用户。ChatState类将实现上述聊天室的行为。
private static class ChatState {
private ArrayList users;
public ChatState() {}
public void broadcast(Event evt) {}
// Mutators
public void addUser(User user) {}
public void removeUser(User user) {}
}
实现很简单,用户列表维护了当前在聊天的用户,事件可以广播给每位用户。
private static class ChatState {
private ArrayList users;
public ChatState() {
this.users = new ArrayList();
}
public void broadcast(Event evt) {
for (User recipient : users)
recipient.dispatch(evt);
}
// Mutators
public void addUser(User user) {
users.add(user);
}
public void removeUser(User user) {
users.remove(user);
}
}
ChatState负责将事件广播给每位注册的用户。
用户状态行为
用户类的基本实现如下。因为用户类能够接收事件,并且需要对接收到的事件进行处理。用户类实现如下。
private static class User {
public String name;
public User(String name) {
this.name = name;
}
// Event demultiplexing
public void dispatch(Event evt) {
if (evt.getClass() == UserMessage.class) {
UserMessage message = (UserMessage) evt;
processMessage(message.user, message.message);
}
}
// Event processing
public void processMessage(User user, String userMessage) {
// Ignore messages by me
if (user.equals(this))
return;
System.out.println(
name + " received message from " +
user.name
);
}
}
在dispatch方法中,Event类型的参数会与UserMessage.class进行比较,如果是UserMessage.class则进行处理。
聊天室与事件处理器
当产生聊天室的相关事件,需要有相关的处理器进行处理。因此需要注册相关事件及其事件处理器。
state.registerChannel(UserArrival.class, new ChatHandler() {
@Override
public void dispatch(Event evt) {
UserArrival arrival = (UserArrival) evt;
arrival.state.addUser(arrival.user);
System.out.println(
arrival.user.name + " has entered the room."
);
}
});
上述代码中将聊天室对象作为事件的一个成员,这样每增加一个事件,就需要关联一个聊天室对象。例如UserArrival, UserDeparture与UserMessage三个事件都必须存储一个聊天室对象的引用。这样就产生了重复代码。为了解决这个问题,可以创建一个包装类来维护聊天室对象的引用。通过ChatHandler保存一个ChatState的引用。
private static class ChatHandler extends Handler {
protected ChatState state;
public ChatHandler(ChatState state) {
this.state = state;
}
}
事件注册方法则更新为如下形式。通过帮助类registerHandlers来将事件和处理方法注册。ChatHandler内部维护了ChatState的引用,而所有关于聊天室的事件处理均由ChatState提供的相关方法来处理。
public static void registerHandlers(EventDispatcher dispatcher, ChatState state) {
dispatcher.registerChannel(UserArrival.class, new ChatHandler(state) {
@Override
public void dispatch(Event evt) {
UserArrival arrival = (UserArrival) evt;
state.addUser(arrival.user);
System.out.println(
arrival.user.name + " has entered the room."
);
}
});
dispatcher.registerChannel(UserDeparture.class, new ChatHandler(state) {
@Override
public void dispatch(Event evt) {
UserDeparture departure = (UserDeparture) evt;
state.removeUser(departure.user);
System.out.println(
departure.user.name + " has left the room."
);
}
});
dispatcher.registerChannel(UserMessage.class, new ChatHandler(state) {
@Override
public void dispatch(Event evt) {
UserMessage message = (UserMessage) evt;
String userMessage =
String.format(
"%s: %s",
message.user.name,
message.message
);
System.out.println(userMessage);
// Broadcast messages
state.broadcast(message);
}
});
}
事件生成
用户发消息会产生响应的事件。产生的事件会放入事件队列中。因此用户类内部增加事件队列的引用。
private static class User {
public Queue eventQueue;
public String name;
public User(Queue eventQueue, String name) {
this.eventQueue = eventQueue;
this.name = name;
}
// Behavioral methods
}
接着为用户增加方法,将产生的事件放入事件队列中。
private static class User {
public Queue eventQueue;
public String name;
public User(Queue eventQueue, String name) {
this.eventQueue = eventQueue;
this.name = name;
}
// Event demultiplexing and handling methods
// Event generation
public void sendMessage(String message) {
eventQueue.add(new UserMessage(this, message));
}
}
这样用户可以进行消息发送,并且不需关心事件的转发。
使用事件队列
接着创建事件队列来存放产生的事件。
import java.util.LinkedList;
// ChatState declaration here
public static void main(String[] args) {
EventDispatcher dispatcher = new Dispatcher();
ChatState state = new ChatState();
Queue eventQueue = new LinkedList();
// Further simulation code such as event handler registration
}
接着创建转发器,转发器不断地从队列中取出事件,然后转发给响应的处理器进行处理。
import java.util.LinkedList;
// ChatState declaration here
public static void main(String[] args) {
EventDispatcher dispatcher = new Dispatcher();
ChatState state = new ChatState();
Queue eventQueue = new LinkedList();
// Further simulation code such as event handler registration
// Possibly generate events beforehand
// Dispatch all queued events
while (!eventQueue.isEmpty()) {
Event evt = eventQueue.remove();
dispatcher.dispatch(evt);
}
}
事件队列并不会与聊天室对象交互,仅用事件生成者进行关联。而用户就是一个事件生生成者。因此为用户对象增加事件队列的引用。
private static class User {
public Queue eventQueue;
public String name;
public User(Queue eventQueue, String name) {
this.eventQueue = eventQueue;
this.name = name;
}
// Behavioral methods
}
当用户生成相关事件后,事件被加入到队列中。
private static class User {
public Queue eventQueue;
public String name;
public User(Queue eventQueue, String name) {
this.eventQueue = eventQueue;
this.name = name;
}
// Event demultiplexing and handling methods
// Event generation
public void sendMessage(String message) {
eventQueue.add(new UserMessage(this, message));
}
}
这样一个基于事件驱动的简单聊天室系统就构建完毕了。
模拟测试
public static void main(String[] args) {
EventDispatcher dispatcher = new EventDispatcher();
ChatState state = new ChatState();
Queue eventQueue = new LinkedList();
registerHandlers(dispatcher, state);
// Initialize users
User foo = new User(eventQueue, "foo");
User bar = new User(eventQueue, "bar");
dispatcher.dispatch(new UserArrival(foo));
dispatcher.dispatch(new UserArrival(bar));
// Enqueue events from individual users
foo.sendMessage("hello, bar!");
bar.sendMessage("hello, foo!");
foo.sendMessage("goodbye, bar!");
// Dispatch all queued events
while (!eventQueue.isEmpty()) {
Event evt = eventQueue.remove();
dispatcher.dispatch(evt);
}
// Finish up simulation
dispatcher.dispatch(new UserDeparture(foo));
dispatcher.dispatch(new UserDeparture(bar));
}
上述代码将产生输出
foo has entered the room.
bar has entered the room.
foo: hello, bar!
bar: hello, foo!
foo: goodbye, bar!
foo has left the room.
bar has left the room.
总结
本文使用Java实现事先驱动框架(一)中实现的事件驱动框架来模拟一个聊天室程序。当事件源产生事件后,将事件加入到了事件队列中。而事件转发器不断的尝试从事件队列中取事件,然后使用事件处理器进行处理。系统基于事件驱动框架构建,通过这个实例对事件驱动模型的简单实现有了一定的认识。