用Java实现事件驱动框架(二)

本文将基于上一篇用Java实现事先驱动框架(一)来模拟实现一个简单的聊天室系统

结构设计

首先确定简单聊天系统的基本对象。系统需要两种基本数据结构:用户与聊天室。

用户类

用户的定义如下。每一个进行聊天的用户都有一个名字。

private static class User {
  public String name;
  public User(String name) {
    this.name = name;
  }
}

聊天室类

聊天室需要维护当前在聊天室中的用户,其结构如下。

private static class ChatState {
  private ArrayList<User> users;
  public ChatState() {}
}

行为

首先思考一下,一个聊天室程序包含的基本事件。

  • 用户到来:当用户进入聊天时产生该事件。
  • 用户离开:当用户离开聊天时产生该事件。
  • 用户发消息:当用户在聊天时发送消息时产生该事件。

聊天室与用户应能够响应并处理与自身相关的事件。下面将详细描述具体实现。

聊天室的行为状态

聊天室应能够处理与其相关的事件。因此需要能够支持下列操作。

  • 添加用户到聊天室
  • 将用户从聊天室删除
  • 广播消息给所有聊天室中的用户

广播消息的接受者是所有的用户。ChatState类将实现上述聊天室的行为。

private static class ChatState {
  private ArrayList<User> 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<User> 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<Event> eventQueue;
  public String name;

  public User(Queue<Event> eventQueue, String name) {
    this.eventQueue = eventQueue;
    this.name = name;
  }
  // Behavioral methods
}

接着为用户增加方法,将产生的事件放入事件队列中。

private static class User {
  public Queue<Event> eventQueue;
  public String name;

  public User(Queue<Event> 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<Event> eventQueue = new LinkedList<Event>();

  // 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<Event> eventQueue = new LinkedList<Event>();

  // 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<Event> eventQueue;
  public String name;

  public User(Queue<Event> eventQueue, String name) {
    this.eventQueue = eventQueue;
    this.name = name;
  }
  // Behavioral methods
}

当用户生成相关事件后,事件被加入到队列中。

private static class User {
  public Queue<Event> eventQueue;
  public String name;

  public User(Queue<Event> 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<Event> eventQueue = new LinkedList<Event>();

  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实现事先驱动框架(一)中实现的事件驱动框架来模拟一个聊天室程序。当事件源产生事件后,将事件加入到了事件队列中。而事件转发器不断的尝试从事件队列中取事件,然后使用事件处理器进行处理。系统基于事件驱动框架构建,通过这个实例对事件驱动模型的简单实现有了一定的认识。

原文地址

http://www.giocc.com/mindispatch-event-driven-framework-in-java-part-2.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值