写点什么

基于 Guava API 实现异步通知和事件回调

作者:Tom弹架构
  • 2021 年 11 月 17 日
  • 本文字数:4936 字

    阅读完需:约 16 分钟

本文节选自《设计模式就该这样学》

1 基于 Java API 实现通知机制

当小伙伴们在社区提问时,如果有设置指定用户回答,则对应的用户就会收到邮件通知,这就是观察者模式的一种应用场景。有些小伙伴可能会想到 MQ、异步队列等,其实 JDK 本身就提供这样的 API。我们用代码来还原这样一个应用场景,首先创建 GPer 类。



/** * JDK提供的一种观察者的实现方式,被观察者 */public class GPer extends Observable{ private String name = "GPer生态圈"; private static GPer gper = null; private GPer(){}
public static GPer getInstance(){ if(null == gper){ gper = new GPer(); } return gper; } public String getName() { return name; } public void publishQuestion(Question question){ System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。"); setChanged(); notifyObservers(question); }}
复制代码


然后创建问题 Question 类。



public class Question { private String userName; private String content;
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }}
复制代码


接着创建老师 Teacher 类。



public class Teacher implements Observer {
private String name;
public Teacher(String name) { this.name = name; }
public void update(Observable o, Object arg) { GPer gper = (GPer)o; Question question = (Question)arg; System.out.println("======================"); System.out.println(name + "老师,你好!\n" + "您收到了一个来自" + gper.getName() + "的提问,希望您解答。问题内容如下:\n" + question.getContent() + "\n" + "提问者:" + question.getUserName()); }}
复制代码


最后编写客户端测试代码。



public static void main(String[] args) { GPer gper = GPer.getInstance(); Teacher tom = new Teacher("Tom"); Teacher jerry = new Teacher("Jerry");
gper.addObserver(tom); gper.addObserver(jerry);
//用户行为 Question question = new Question(); question.setUserName("张三"); question.setContent("观察者模式适用于哪些场景?");
gper.publishQuestion(question);}
复制代码


运行结果如下图所示。


2 基于 Guava API 轻松落地观察者模式

笔者向大家推荐一个实现观察者模式的非常好用的框架,API 使用也非常简单,举个例子,首先引入 Maven 依赖包。



<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version></dependency>
复制代码


然后创建侦听事件 GuavaEvent。



/** * Created by Tom */public class GuavaEvent { @Subscribe public void subscribe(String str){ //业务逻辑 System.out.println("执行subscribe方法,传入的参数是:" + str); }
}
复制代码


最后编写客户端测试代码。




/** * Created by Tom */public class GuavaEventTest { public static void main(String[] args) { EventBus eventbus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventbus.register(guavaEvent); eventbus.post("Tom"); }
}
复制代码

3 使用观察者模式设计鼠标事件响应 API

再来设计一个业务场景,帮助小伙伴们更好地理解观察者模式。在 JDK 源码中,观察者模式的应用也非常多。例如 java.awt.Event 就是观察者模式的一种,只不过 Java 很少被用来写桌面程序。我们用代码来实现一下,以帮助小伙伴们更深刻地了解观察者模式的实现原理。首先,创建 EventListener 接口。



/** * 观察者抽象 * Created by Tom. */public interface EventListener {
}
复制代码


创建 Event 类。




/** * 标准事件源格式的定义 * Created by Tom. */public class Event { //事件源,动作是由谁发出的 private Object source; //事件触发,要通知谁(观察者) private EventListener target; //观察者的回应 private Method callback; //事件的名称 private String trigger; //事件的触发事件 private long time;
public Event(EventListener target, Method callback) { this.target = target; this.callback = callback; }
public Object getSource() { return source; }
public Event setSource(Object source) { this.source = source; return this; }
public String getTrigger() { return trigger; }
public Event setTrigger(String trigger) { this.trigger = trigger; return this; }
public long getTime() { return time; }
public Event setTime(long time) { this.time = time; return this; }
public Method getCallback() { return callback; }
public EventListener getTarget() { return target; }
@Override public String toString() { return "Event{" + "source=" + source + ", target=" + target + ", callback=" + callback + ", trigger='" + trigger + '\'' + ", time=" + time + '}'; }}
复制代码


创建 EventContext 类。



/** * 被观察者的抽象 * Created by Tom. */public abstract class EventContext { protected Map<String,Event> events = new HashMap<String,Event>();
public void addListener(String eventType, EventListener target, Method callback){ events.put(eventType,new Event(target,callback)); }
public void addListener(String eventType, EventListener target){ try { this.addListener(eventType, target, target.getClass().getMethod("on"+toUpperFirstCase(eventType), Event.class)); }catch (NoSuchMethodException e){ return; } }
private String toUpperFirstCase(String eventType) { char [] chars = eventType.toCharArray(); chars[0] -= 32; return String.valueOf(chars); }
private void trigger(Event event){ event.setSource(this); event.setTime(System.currentTimeMillis());
try { if (event.getCallback() != null) { //用反射调用回调函数 event.getCallback().invoke(event.getTarget(), event); } }catch (Exception e){ e.printStackTrace(); } }
protected void trigger(String trigger){ if(!this.events.containsKey(trigger)){return;} trigger(this.events.get(trigger).setTrigger(trigger)); }}
复制代码


然后创建 MouseEventType 接口。



/** * Created by Tom. */public interface MouseEventType { //单击 String ON_CLICK = "click";
//双击 String ON_DOUBLE_CLICK = "doubleClick";
//弹起 String ON_UP = "up";
//按下 String ON_DOWN = "down";
//移动 String ON_MOVE = "move";
//滚动 String ON_WHEEL = "wheel";
//悬停 String ON_OVER = "over";
//失去焦点 String ON_BLUR = "blur";
//获得焦点 String ON_FOCUS = "focus";}
复制代码


创建 Mouse 类。



/** * 具体的被观察者 * Created by Tom. */public class Mouse extends EventContext {
public void click(){ System.out.println("调用单击方法"); this.trigger(MouseEventType.ON_CLICK); }
public void doubleClick(){ System.out.println("调用双击方法"); this.trigger(MouseEventType.ON_DOUBLE_CLICK); }
public void up(){ System.out.println("调用弹起方法"); this.trigger(MouseEventType.ON_UP); }
public void down(){ System.out.println("调用按下方法"); this.trigger(MouseEventType.ON_DOWN); }
public void move(){ System.out.println("调用移动方法"); this.trigger(MouseEventType.ON_MOVE); }
public void wheel(){ System.out.println("调用滚动方法"); this.trigger(MouseEventType.ON_WHEEL); }
public void over(){ System.out.println("调用悬停方法"); this.trigger(MouseEventType.ON_OVER); }
public void blur(){ System.out.println("调用获得焦点方法"); this.trigger(MouseEventType.ON_BLUR); }
public void focus(){ System.out.println("调用失去焦点方法"); this.trigger(MouseEventType.ON_FOCUS); }}
复制代码


创建回调方法 MouseEventLisenter 类。



/** * 观察者 * Created by Tom. */public class MouseEventListener implements EventListener {

public void onClick(Event e){ System.out.println("===========触发鼠标单击事件==========" + "\n" + e); }
public void onDoubleClick(Event e){ System.out.println("===========触发鼠标双击事件==========" + "\n" + e); }
public void onUp(Event e){ System.out.println("===========触发鼠标弹起事件==========" + "\n" + e); }
public void onDown(Event e){ System.out.println("===========触发鼠标按下事件==========" + "\n" + e); }
public void onMove(Event e){ System.out.println("===========触发鼠标移动事件==========" + "\n" + e); }
public void onWheel(Event e){ System.out.println("===========触发鼠标滚动事件==========" + "\n" + e); }
public void onOver(Event e){ System.out.println("===========触发鼠标悬停事件==========" + "\n" + e); }
public void onBlur(Event e){ System.out.println("===========触发鼠标失去焦点事件==========" + "\n" + e); }
public void onFocus(Event e){ System.out.println("===========触发鼠标获得焦点事件==========" + "\n" + e); }
}
复制代码


最后编写客户端测试代码。



public static void main(String[] args) { EventListener listener = new MouseEventListener();
Mouse mouse = new Mouse(); mouse.addListener(MouseEventType.ON_CLICK,listener); mouse.addListener(MouseEventType.ON_MOVE,listener);
mouse.click(); mouse.move(); }
复制代码

关注微信公众号『 Tom 弹架构 』回复“设计模式”可获取完整源码。


【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦


本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom 弹架构 』可获取更多技术干货!

发布于: 17 小时前阅读数: 12
用户头像

Tom弹架构

关注

不只做一个技术者,更要做一个思考者 2021.10.22 加入

畅销书作者,代表作品: 《Spring 5核心原理与30个类手写实战》 《Netty 4核心原理与手写RPC框架实战》 《设计模式就该这样学》

评论

发布
暂无评论
基于Guava API实现异步通知和事件回调