定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象 都得到通知并被自动更新。
Spring 中 Observer 模式常用的地方是 Listener 的实现。如 ApplicationListener。
归类 | 特点 | 穷举 |
行为模式 | 一般由两个对象组成:发布者和订阅者(观察者)。 观察者通常有一个回调,也可以没有。 | 监听器、日志收集、邮件通知 |
Demo
事件与事件监听器(core包)
/**
* @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 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;
}
@Override
public String toString() {
return "Event{" +
"\n\tsource=" + source +
",\n \t target=" + target +
",\n \t callback=" + callback +
",\n \t trigger='" + trigger + '\'' + "\n" +
'}';
}
public long getTime() {
return time;
}
Event setTime(long time) {
this.time = time;
return this;
}
Event setSource(Object source) {
this.source = source;
return this;
}
Event setTrigger(String trigger) {
this.trigger = trigger;
return this;
}
}
/**
* @description:事件的注册和监听
**/
public class EventListener {
//Map相当于一个注册器
protected Map<Enum,Event> events = new HashMap<>();
public void addListenter(Enum eventType, Object target, Method callback){
//注册事件,将监听事件放在events的map中
//用反射调用这个方法
events.put(eventType,new Event(target,callback)); //target 就是observer method 就是advice 在传参数时就传进来了
}
private void trigger(Event e){
e.setSource(this);
e.setTime(System.currentTimeMillis());
//通过反射调用
try {
Method callback = e.getCallback();
callback.invoke(e.getTarget(),e); //invoke(Object obj, Object...args) obj 表示哪个对象,args表示方法的参数 callback 表示是哪个方法,此处 callback表示的advice方法,e.getTarget表示的observer 即 advice方法所在的类,e 表示method的参数
} catch (Exception e1) {
e1.printStackTrace();
}
}
//触发事件的源头
protected void trigger(Enum call){
if(!this.events.containsKey(call)){
return;
}
//此处调用trigger方法,并把 trigger 的值设置进去
trigger(this.events.get(call).setTrigger(call.toString()));
}
}
Observer(观察者)与Subject(被观察者) (subject 包)
/**
* @description:观察者
**/
public class Observer {
public void advice(Event e){
System.out.println("=====触发事件,打印日志=====\n" + e );
}
}
/**
* @description:被观察者
**/
public class Subject extends EventListener {
//通常的话,采用动态代理来实现监控,避免了代码的侵入
public void add(){
System.out.println("调用添加的方法");
trigger(SubjectEventType.ON_ADD);
}
public void remove(){
System.out.println("调用删除方法");
trigger(SubjectEventType.ON_REMOVE);
}
}
/**
* @description: 被观察者事件枚举
**/
public enum SubjectEventType {
ON_ADD,
ON_REMOVE,
ON_EDIT,
ON_QUERY
}
/**
* @description:观察者测试类
**/
public class ObserverTest {
public static void main(String[] args) {
try {
//观察者
Observer observer = new Observer();
//获取advice getMethod 时就获取了方法名和参数列表
Method advice = Observer.class.getMethod("advice", new Class<?>[]{Event.class});
//被观察者
Subject subject = new Subject();
//注册监听事件
subject.addListenter(SubjectEventType.ON_ADD,observer,advice);
subject.addListenter(SubjectEventType.ON_EDIT,observer,advice);
subject.addListenter(SubjectEventType.ON_QUERY,observer,advice);
subject.addListenter(SubjectEventType.ON_REMOVE,observer,advice);
//调用方法
subject.add();
subject.remove();
}catch(Exception e){
}
}
}
使用观察者模式实现鼠标点击事件
/**
* @description:被观察者
**/
public class Mouse extends EventListener {
public void click(){
System.out.println("鼠标单击");
this.trigger(MouseEventType.ON_CLICK);
this.trigger(MouseEventType.ON_CLICK_2);
}
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 wheel(){
System.out.println("鼠标滚动");
this.trigger(MouseEventType.ON_WHEEL);
}
public void move(){
System.out.println("鼠标移动");
this.trigger(MouseEventType.ON_MOVE);
}
public void over(){
System.out.println("鼠标悬停");
this.trigger(MouseEventType.ON_OVER);
}
}
/**
* @description:回调响应的逻辑,由自己实现, 观察者
**/
public class MouseEventCallBack {
public void onClick(Event e){
System.out.println("这是鼠标单击以后要执行的逻辑");
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);
}
}
/**
* @description:第二个观察者
**/
public class MouseEventCallBack2 {
public void onClick(Event e){
System.out.println("这是第二个观察者中鼠标单击以后要执行的逻辑");
System.out.println("======触发了鼠标单击事件========\n" + e);
}
}
/**
* @description: 鼠标事件枚举类型
**/
public enum MouseEventType {
ON_CLICK,
ON_CLICK_2,
ON_DOUBLE_CLICK,
ON_UP,
ON_DOWN,
ON_WHEEL,
ON_MOVE,
ON_OVER
}
public class MouseTest {
/**
* var input = document.getElementById("username");
* addLisenter相当于注册了一个事件并且给了一个注册逻辑
* input.addLisenter("click",function(){
* alert("鼠标点击了这个文本框");
* });
*/
public static void main(String[] args) {
// 观察者和被观察者之间没有必然的联系,注册的时候,才会产生联系。
// 观察者模式 最重要的就是动态解耦
//代码时完全可以写在Mouse里面的,但是当Mouse产生的时候,callBack 还没有呢。所以就不可能再Mouse里面写代码了
//因为Mouse产生后,我们在里面改不了了。所以才把这两个类隔离出来,进行动态解耦。
//mouse 在前callback 在后,mouse并不清楚callback 回调要实现什么要的逻辑
try {
//回调事件1
MouseEventCallBack callBack = new MouseEventCallBack();
//反射获取回调方法1
Method onClick = MouseEventCallBack.class.getMethod("onClick", Event.class);
//回调事件2
MouseEventCallBack2 callBack2 = new MouseEventCallBack2();
//反射获取回调事件2
Method onClick2 = MouseEventCallBack2.class.getMethod("onClick", Event.class);
//人为调用鼠标的单击事件
Mouse mouse = new Mouse();
//增加callback的回调事件
mouse.addListenter(MouseEventType.ON_CLICK, callBack,onClick );
//增加callback2的回调事件
mouse.addListenter(MouseEventType.ON_CLICK_2,callBack2,onClick2);
mouse.click();
} catch (Exception e) {
e.printStackTrace();
}
}
}