设计模式-观察者模式 设计模式-观察者模式

定义

观察者模式(Observer Pattern)又叫发布-订阅模式。定义一种一对多的依赖关系。
一个主题对象可以被多个观察者对象同事监听,使得每当主题对象状态发生变化时,所有依赖于它的对象都会得到通知并被自动更新

属于行为模式。

适用场景

  1. 当一个抽象模型包含2个方面内容,其中一个方面依赖于另一个方面;
  2. 其他一个或多个对象的变化依赖于另一个对象的变化;
  3. 实现类似广播机制的功能,无需知道具体收听者,只需分化
  4. 多层级嵌套,形成一种链式触发机制,使得事件具备跨域通知

代码示例

手写观察者模式

拿鼠标举例

  1. 创建事件类
package com.black.design.pattern.observer.mouse;

import java.lang.reflect.Method;

// 事件类
public class Event {
    // 事件名称
    private String name;
    // 事件源
    private Object source;
    // 事件触发,通知目标(观察者)
    private Object target;
    // 观察者回调函数
    private Method callback;
    // 创建事件的时间
    private String createTime;
    
    public Event(String name) {
        this.name = name;
    }
    
    public Event(Object target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    public String getName() {
        return name;
    }

    public Object getSource() {
        return source;
    }

    public Object getTarget() {
        return target;
    }

    public Method getCallback() {
        return callback;
    }

    public String getCreateTime() {
        return createTime;
    }

    public Event setName(String name) {
        this.name = name;
        return this;
    }

    public Event setSource(Object source) {
        this.source = source;
        return this;
    }

    public Event setTarget(Object target) {
        this.target = target;
        return this;
    }

    public Event setCallback(Method callback) {
        this.callback = callback;
        return this;
    }

    public Event setCreateTime(String createTime) {
        this.createTime = createTime;
        return this;
    }
}

  1. 创建事件监听类,当事件触发时,默认调用 回调类对应的on方法
package com.black.design.pattern.observer.mouse;

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<String, Event>();
    // 感兴趣的事件
    public void interestEvent(String eventType, Object target, Method callback) {
        events.put(eventType, new Event(target, callback));
    }
    // 感兴趣的事件
    public void interestEvent(String eventType, Object target) throws NoSuchMethodException, SecurityException {
        // 回调方法 
        Method callback = target.getClass().getMethod("on" + toUpperFirstChar(eventType), Event.class);
        interestEvent(eventType, target, callback);
    }
    
    // 首字母转为大写
    private String toUpperFirstChar(String eventType) {
       char [] chars = eventType.toCharArray();
       chars[0] -=32;
       return new String(chars);
    }
    
    // 触发事件
    protected void triger(String triger) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (!events.containsKey(triger)) {return;}
        Event t = events.get(triger);
        t.setSource(this);
        t.setCreateTime(String.valueOf(System.nanoTime()));
        
        if(t.getCallback() != null) {
            t.getCallback().invoke(t.getTarget(), t);
        }
         }
}

  1. 创建鼠标事件类型

package com.black.design.pattern.observer.mouse;

// 事件类型
public class MouseEventType {
    // 单击事件
    public static final String ON_CLICK="click";
 // 双击事件
    public static final String ON_DOUBLE_CLICK="doubleClick";
 // 移动事件
    public static final String ON_MOVE="move";
 // 滑动事件
    public static final String ON_SCROLL="scroll";
}

  1. 创建鼠标类
package com.black.design.pattern.observer.mouse;

import java.lang.reflect.InvocationTargetException;

// 鼠标
public class Mouse{
    // 鼠标监听器
    private MouseEventListener listener;
    
    // 注册监听器
    public void registerListener(MouseEventListener listener) {
        this.listener = listener;
    }
    
    // 单击
    public void click() {
        System.out.println("单击鼠标");
        triger(MouseEventType.ON_CLICK);
    }
 // 双击
    public void doubleClick() {
        System.out.println("双击鼠标");
        triger(MouseEventType.ON_DOUBLE_CLICK);
    }
 // 移动
    public void move() {
        System.out.println("鼠标移动");
        triger(MouseEventType.ON_MOVE);
    }
    
   // scroll
    public void scroll() {
        System.out.println("鼠标滑动");
        triger(MouseEventType.ON_SCROLL);
     }
    
    // 触发监听器
    private void triger(String eventType) {
        if(listener != null) {
            try {
                listener.triger(eventType);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
    }
}

  1. 创建回调类
package com.black.design.pattern.observer.mouse;
// 回调类
public class MouseCallBack {

	// 点击回调
    public void onClick(Event t) {
        System.out.println("回调 on click");
    }
    // 双击回调
    public void onDoubleClick(Event t) {
		
		
        System.out.println("回调 on double click");
    }
}

  1. 测试
package com.black.design.pattern.observer.mouse;

import java.awt.event.MouseListener;

// 测试类
public class MouseTest {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
        
        // 实例化鼠标
        Mouse mouse = new Mouse();
        
        // 回调类
        MouseCallBack callBack = new MouseCallBack();
       // 实例化鼠标事件监听器
        MouseEventListener listener = new MouseEventListener();
        // 感兴趣的事件是 单击,如果鼠标单击则会回调 callBack 类
        listener.interestEvent(MouseEventType.ON_CLICK, callBack);
        
        // 鼠标注册监听器,监听器监听鼠标的行为
        mouse.registerListener(listener);
        
        // 单击鼠标
        System.out.println("======单击鼠标======");
        mouse.click();
      
      // 双击鼠标
        System.out.println("======双击鼠标======");
        mouse.doubleClick();
        
    }
}

运行结果:

======单击鼠标======
单击鼠标
监听到来自com.black.design.pattern.observer.mouse.MouseEventListener的click事件
事件回调onClick方法
======双击鼠标======
双击鼠标

JDK 提供的观察者模式

  1. 创建一个 Observer 接口的实现类
package com.black.design.pattern.observer.jdk;

import java.util.Observable;
import java.util.Observer;

/**
 * 观察者
 * @author black
 *
 */
public class MyObserver implements Observer{
	
	private String name;

	public MyObserver(String name) {
		this.name = name;
	}

	@Override
	public void update(Observable o, Object arg) {
		Target target = (Target)o;
		System.out.println(name + " 接收到信息: " + arg);
	}
}

  1. 创建 Observable 类的子类
package com.black.design.pattern.observer.jdk;

import java.util.Observable;
import java.util.Observer;

/**
 * 被观察者
 * @author black
 *
 */
public class Target extends Observable {

	@Override
	public synchronized void addObserver(Observer o) {
		super.addObserver(o);
	}
	
	public void postNotice(String notice) {
		System.out.println("发布消息: " + notice);
		setChanged();
		notifyObservers(notice);
	}
}
  1. 测试类
package com.black.design.pattern.observer.jdk;

/**
 * 观察者测试类
 * @author black
 *
 */
public class ObserverTest {

	public static void main(String[] args) {
		// 创建被观察者 target
		Target target = new Target();
		// 创建2个观察者 blackMyObserver 和 xiaomingMyObserver
		MyObserver blackMyObserver = new MyObserver("black");
		MyObserver xiaomingMyObserver = new MyObserver("xiaoming");
		target.addObserver(blackMyObserver);
		target.addObserver(xiaomingMyObserver);
		// 被观察者 target 发出一个通知
		System.out.println("=======第1条通知=========");
		target.postNotice("明天下午8点停电!");
		System.out.println("=======第2条通知=========");
		target.postNotice("后天早上8点点打卡!");
	}
}

测试结果:

=======第1条通知=========
发布消息: 明天下午8点停电!
xiaoming 接收到信息: 明天下午8点停电!
black 接收到信息: 明天下午8点停电!
=======第2条通知=========
发布消息: 后天早上8点点打卡!
xiaoming 接收到信息: 后天早上8点点打卡!
black 接收到信息: 后天早上8点点打卡!

Guava 提供的观察者模式

  1. 引入 guava jar 包
   <dependency>
		<groupId>com.google.guava</groupId>
		<artifactId>guava</artifactId>
		<version>20.0</version>
	</dependency>
  1. 创建 事件类
package com.black.design.pattern.observer.guava;

import com.google.common.eventbus.Subscribe;

/**
 * 使用@Subscribe注解,订阅观察的方法
 * @author black
 *
 */
public class GuavaEvent {

	@Subscribe
	public void subscribe(String str) {
		//执行业务逻辑
		System.out.println("调用 subscribe,传入参数:" + str);
	}
}

  1. 创建测试类

package com.black.design.pattern.observer.guava;

import com.google.common.eventbus.EventBus;

public class GuavaEventTest {

	public static void main(String[] args) {
		// 创建事件总线
		EventBus eventBus = new EventBus();
		// 创建自定义的事件
		GuavaEvent guavaEvent = new GuavaEvent();
		// 将自定义的事件注册到 EventBus 中
		eventBus.register(guavaEvent);
		// EventBus 发送消息,各个事件会接受到这个消息
		eventBus.post("black");
	}
}

测试结果:

调用 subscribe,传入参数:black