设计模式(七)之观察者模式(Observer Pattern)深入浅出
观察者模式的定义:
- 观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。
- 属于行为型模式。
- 观察者模式有时也叫发布订阅模式。
观察者模式的适用场景:
- 观察者模式主要用于在关联行为之间建立一套触发机制的场景
观察者模式源码案例:
- JDK源码中的java.awt.Event、Observable、EventListener
- Spring源码中ContextLoaderListener被监听者,实现了javax.servlet中的ServletContextListener,而它继承了JDK中的EventListener,监听者ApplicationContextAware
- J2ee的javax.servlet中的ServletContextListener
- MQ是观察者模式具体的应用
JDK的Observable实现观察者模式
案例:学生在平台发布问题后,指定的老师会收到消息
发布问题的平台
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:04 * @description: 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); } }
问题实体类
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:07 * @description: */ 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; } }
老师(观察者)
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:11 * @description: 观察者 */ public class Teacher implements Observer { private String name; public Teacher(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { GPer gPer = (GPer) o; Question question = (Question) arg; System.out.println("------------------"); System.out.println(this.name + "老师,您好!\n"+ "您收到了一个来自“" + gPer.getName() +"”的提问,希望您解答,问题内容如下:\n" + question.getContent() + "\n" + "提问者:"+ question.getUsername()); } }
测试用例
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:18 * @description: */ public class ObserverTest { public static void main(String[] args) { GPer gPer = GPer.getInstance(); Teacher tom = new Teacher("Tom"); Question question = new Question(); question.setUsername("小明"); question.setContent("观察者模式适用哪些场景?"); gPer.addObserver(tom); gPer.publishQuestion(question); } }
手动实现观察者模式
案例:鼠标事件监听
事件封装类
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:31 * @description: 监听器的一种包装,标准事件源的定义 */ public class Event { /** * 事件源,事件是由谁发起的,保存起来 */ private Object source; /** * 事件触发,要通知谁 */ private Object target; /** * 事件触发,要做什么动作,回调 */ private Method callback; /** * 事件的名称,触发的是什么事件 */ private String trigger; /** * 事件触发的时间 */ private long time; public Event(Object target, Method callback) { this.target = target; this.callback = callback; } public Object getSource() { return source; } public void setSource(Object source) { this.source = source; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Method getCallback() { return callback; } public void setCallback(Method callback) { this.callback = callback; } public String getTrigger() { return trigger; } public void setTrigger(String trigger) { this.trigger = trigger; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } @Override public String toString() { return "Event{" + "source=" + source + ", target=" + target + ", callback=" + callback + ", trigger='" + trigger + '\'' + ", time=" + time + '}'; } }
监听器(Observable)
/** * @author: ZhouCong * @date: Create in 2021/1/26 11:37 * @description: 监听器,他就是观察者 */ public class EventListener { /** * JDK底层的Listener通常也是这样来设计的 */ protected Map<String, Event> events = new HashMap<>(); /** * 事件名称和一个目标对象来触发事件 * * @param eventType * @param target */ public void addListener(String eventType, Object target) { try { this.addListener(eventType, target, target.getClass().getMethod("on" + toUpperFirstCase(eventType),Event.class)); } catch (Exception e) { e.printStackTrace(); } } public void addListener(String eventType, Object target, Method callback) { // 注册事件 events.put(eventType, new Event(target, callback)); } /** * 触发,只要有动作就触发 * * @param event */ private void trigger(Event event) { event.setSource(this); event.setTime(System.currentTimeMillis()); try { // 发起回调 event.getCallback().invoke(event.getTarget(), event); } catch (Exception e) { e.printStackTrace(); } } /** * 事件名称触发 * * @param trigger */ protected void trigger(String trigger) { if (!this.events.containsKey(trigger)) { return; } Event e = this.events.get(trigger); e.setTrigger(trigger); trigger(e); } /** * 首字母大写 * * @param str * @return */ private String toUpperFirstCase(String str) { char[] chars = str.toCharArray(); chars[0] -= 32; return String.valueOf(chars); } }
JDK中的Observable使用Vector<Observer> 储存事件,循环遍历调用update方法触发,这里用Map存储,避免循环判断调用
鼠标事件
/** * @author: ZhouCong * @date: Create in 2021/1/26 12:09 * @description: */ public class Mouse extends EventListener { 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); } }
鼠标事件类型
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"; }
鼠标事件发生后的回调
/** * @author: ZhouCong * @date: Create in 2021/1/26 12:18 * @description: 自己写的逻辑,用于回调 */ public class MouseEventCallback { 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); } }
测试用例
/** * @author: ZhouCong * @date: Create in 2021/1/26 12:28 * @description: */ public class MouseEventTest { public static void main(String[] args) { MouseEventCallback callback = new MouseEventCallback(); Mouse mouse = new Mouse(); // 添加监听 mouse.addListener(MouseEventType.ON_CLICK,callback); mouse.addListener(MouseEventType.ON_FOCUS,callback); mouse.click(); mouse.focus(); } }
Guava实现观察者模式
添加依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency>
事件类
/** * @author: ZhouCong * @date: Create in 2021/1/26 15:23 * @description: */ public class GuavaEvent { @Subscribe public void subscribe(String str){ System.out.println("执行subscribe方法,传入的参数是:" + str); } }
测试用例
/** * @author: ZhouCong * @date: Create in 2021/1/26 15:26 * @description: */ public class GuavaTest { public static void main(String[] args) { // 消息总线 EventBus eventBus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventBus.register(guavaEvent); eventBus.post("Hello"); // Guava实现观察者类似从Struts到SpringMVC的升级 // 因为Struts面向的类,而SpringMVC面向的是方法 // 前面两者面向的是类,Guava面向的是方法 // Guava是能够轻松落地观察者模式的一种解决方案 } }
运行结果
总结
观察者模式的优点:
- 观察者和被观察者之间建立了一个抽象的耦合。
- 观察者模式支持广播通信。
观察者模式的缺点:
- 观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
- 使用要得当,要避免循环调用。
以上对观察者模式的介绍到此结束,欢迎批评指正。 附:源码地址