观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
观察者是又称为发布订阅模式。观察者一般是指第三方,负责通知订阅者消息。
案例
Observer 观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者
1 2 3 4
| public interface Observer { void update(String message); }
|
Subject 抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notify(String message); }
|
ConcreteSubject 具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class ConcreteSubject implements Subject { private final List<Observer> list = new ArrayList<>();
@Override public void attach(Observer observer) { list.add(observer); }
@Override public void detach(Observer observer) { list.remove(observer); }
@Override public void notify(String message) { for (Observer observer : list) { observer.update(message); } } }
|
User 订阅者(被观察者)
1 2 3 4 5 6 7 8 9 10 11 12
| public class User implements Observer { private String name;
public User(String name) { this.name = name; }
@Override public void update(String message) { System.out.println(name + " 收到消息:" + message); } }
|
Test 测试类
1 2 3 4 5 6 7 8 9 10 11 12
| public class testObserver { public static void main(String[] args) { ConcreteSubject concreteSubject = new ConcreteSubject(); User user1 = new User("张三"); User user2 = new User("李四"); concreteSubject.attach(user1); concreteSubject.attach(user2);
concreteSubject.notify("快来帮我砍一刀"); } }
|
观察者模式在JDK中的应用
在JDK中的java.awt.Event 就是观察者模式的一种,使用观察者模式和反射实现方法回调。
Event:事件实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| 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 Event setTrigger(String trigger) { this.trigger = trigger; return this; }
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 + '}'; } }
|
EventListener:事件监听类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class EventListener { protected Map<String, Event> events = new HashMap<>(); public void addListener(String eventType, Object target) { try { addListener(eventType, target, target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class)); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
private static String toUpperFirstCase(String str) { char[] chars = str.toCharArray(); chars[0] -= 32; return String.valueOf(chars); }
public void addListener(String eventType, Object target, Method callback) { events.put(eventType, new Event(target, callback)); }
private void trigger(Event event) { event.setSource(this); event.setTime(System.currentTimeMillis());
if (event.getCallback() != null) { try { event.getCallback().invoke(event.getTarget(), event); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
protected void trigger(String eventType) { if (!this.events.containsKey(eventType)) { return; } trigger(this.events.get(eventType).setTrigger(eventType)); } }
|
MouseEventType:鼠标事件常量
1 2 3 4 5 6 7 8 9
| public interface MouseEventType { String ON_CLICK = "click";
String ON_DOUBLE_CLICK = "doubleClick";
String ON_UP = "up";
String ON_DOWN = "down"; }
|
Mouse:鼠标事件实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Mouse extends EventListener {
public void click() { System.out.println("调用单击方法"); this.trigger(MouseEventType.ON_CLICK); }
public void dClick() { 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); } }
|
MouseEventCallback:鼠标事件回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class MouseEventCallback { public void onClick(Event e) { System.out.println("============= 触发单击事件 =============\n" + e.toString()); }
public void onDoubleClick(Event e) { System.out.println("============= 触发双击事件 =============\n" + e.toString()); }
public void onUp(Event e) { System.out.println("============= 触发弹起事件 =============\n" + e.toString()); }
public void onDown(Event e) { System.out.println("============= 触发按下事件 =============\n" + e.toString()); } }
|
Test 测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ObserverTest { public static void main(String[] args) { MouseEventCallback callback = new MouseEventCallback(); Mouse mouse = new Mouse(); mouse.addListener(MouseEventType.ON_CLICK, callback); mouse.addListener(MouseEventType.ON_UP, callback); mouse.addListener(MouseEventType.ON_DOWN, callback); mouse.addListener(MouseEventType.ON_DOUBLE_CLICK, callback);
mouse.click(); mouse.down(); } }
|
观察者模式在Spring源码中的应用
Spring 中的 ContextLoaderListener 实现了 ServletContextListener 接口,ServletContextListener 接口又继承了 EventListener,在 JDK 中 EventListener 有非常广泛的应用。我们可以看一下源代码,ContextLoaderListener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); }
@Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
|
ServletContextListener
1 2 3 4 5 6
| package javax.servlet; import java.util.EventListener; public interface ServletContextListener extends EventListener { public void contextInitialized(ServletContextEvent sce); public void contextDestroyed(ServletContextEvent sce); }
|
EventListener
1 2
| package java.util; public interface EventListener {}
|
基于GuavaApi轻松实现观察者模式
Guava 是一个实现观察者模式非常好的框架。
1 2 3 4 5
| <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency>
|
GuavaEvent :监听事件(这是观察者模式的核心)
1 2 3 4 5 6 7 8
| import com.google.common.eventbus.Subscribe;
public class GuavaEvent { @Subscribe public void subscribe(String str) { System.out.println("执行subscribe方法,传入的参数是:" + str); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11
| import com.google.common.eventbus.EventBus; import org.springframework.web.context.ContextLoaderListener;
public class GuavaEventTest { public static void main(String[] args) { EventBus eventBus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventBus.register(guavaEvent); eventBus.post("Tom"); } }
|
优缺点
优点
(1)在观察者和被观察者之间建立了一个抽象的耦合。
(2)支持广播通信。
缺点
(1)观察者之间存在过多细节依赖、时间消耗过多,让程序更复杂了。
(2)使用不当会出现循环调用。