观察者模式还有一个名字叫发布-订阅模式,我们熟悉的消息队列如ActiveMQ,就是采用的这种设计模式,总的来说观察者模式主要难理解的地方有两个,一个是发布者对一个或多个观察者的管理,另一个就是实现消息发布者和订阅者之间的解耦。
比较简单的实例已经有相关博客总结的非常OK了,如这篇:观察者模式简单实例,该片博客中的实例虽然简单,但是却非常具有代表性,能较好的解释观察模式的几个概念。下面在这篇博客的基础上实现其实例,并对实例做一些简单的改动
上类图:
IPublish和ICustomer代码:
public interface IPublisher {
public void registerObserver(ICustomer o);
public void removeObserver(ICustomer o);
public void notifyObserver();
}
public interface ICustomer {
public void receiveMessage(String message);
}
Publish和Customer代码:
public class Publisher implements IPublisher {
private List observers;
private String message;
public Publisher(List observers){
this.observers = observers;
}
@Override
public void registerObserver(ICustomer o) {
observers.add(o);
}
@Override
public void removeObserver(ICustomer o) {
if(!observers.isEmpty()){
observers.remove(o);
}
}
@Override
public void notifyObserver() {
for(ICustomer o:this.observers){
o.receiveMessage(message);
}
}
/**
* 设置了消息立即通知观察者
* @param message
*/
public void setInformation(String message){
this.message = message;
System.out.println("公众号更新消息如下:"+message);
//通知观察者
notifyObserver();
}
}
public class Customer implements ICustomer{
private String name;
private String message;
public Customer(String name){
this.name = name;
}
@Override
public void receiveMessage(String message) {
this.message = message;
read();
}
public void read(){
System.out.println(name+":收到推送消息:"+message);
}
}
测试实例:
public class ClientTest {
public static void main(String[] args) {
//这里需要模拟一个发布者,多个订阅者
//消息订阅者
ICustomer cus01 = new Customer("body01");
ICustomer cus02 = new Customer("body02");
ICustomer cus03 = new Customer("body03");
//消息发布者
List observers = new ArrayList();
observers.add(cus01);
observers.add(cus02);
observers.add(cus03);
Publisher subscribe = new Publisher(observers);
//更新了消息之后,会自动通知消费者
subscribe.setInformation("message01 ---这是人性的扭曲还是道德的沦丧");
System.out.println("cus01 取消关注公众号");
observers.remove(cus01);
subscribe.setInformation("message02 ---震惊,一群男女光天化日下尽然干这事......");
}
}
运行结果:
一个比较简单的实例,但是却将观察者模式的相关对象展示的比较清晰,回到文章的开头,观察模式会有两个难理解的地方,一个是针对多个订阅者的维护,上述实例是通过在发布者对象中维护一个list来完成对多个订阅者的维护,针对解耦,上述实例是定义了两个接口,消息发布接口和消息订阅者接口,两者都可以自动扩展自己的对象——如果用于需要关注另一个公众号,只需要在加一个公众号类实现消息发布接口即可。
其实见到观察者模式,还有一个不得不提到的就是Listener(监听器),这在spring中也大量用到,这里需要进一步理解这个概念,如果提到监听器,第一反应就想到了之前C/S模式下的各种控件监听事件的编写,所以这里就利用一个简单的鼠标事件例子来进一步解释观察者模式
类图:
这个实例中Mouse就是其实就类似消息的发布者,点击了鼠标,相当于发出了消息,Event会根据事件类型去调用相应的处理方式,就是MouseEventCallBack,所以这里的MouseEventCallBack就相当于是订阅者,而其中的EventListener相当于是消息发布者维护消息订阅者的一张表而已。根据不同的事件去调动不同的Event对象,Event对象根据callback方法去调用MouseEventCallback中相应的方法。各个类的代码如下:
Mouse
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 wheel(){
System.out.println("鼠标滚动");
this.trigger(MouseEventType.ON_WHEEL);
}
public void hover(){
System.out.println("鼠标悬停");
this.trigger(MouseEventType.ON_HOVER);
}
}
MouseEventCallback
public class MouseEventCallback {
public void onClick(Event e){
System.out.println("=================触发了鼠标单击事件=================\n");
}
public void onDoubleClick(Event e){
System.out.println("=================触发了鼠标双击事件=================\n");
}
public void onUp(Event e){
System.out.println("=================触发了鼠标弹起事件=================\n");
}
public void onDown(Event e){
System.out.println("=================触发了鼠标下压事件=================\n");
}
public void onWheel(Event e){
System.out.println("=================触发了鼠标滚动事件=================\n");
}
public void onHover(Event e){
System.out.println("=================触发了鼠标悬停事件=================\n");
}
public void onMove(Event e){
System.out.println("=================触发了鼠标移动事件=================\n");
}
}
EventListener
public class EventListener {
protected Map events = new HashMap();
public void addListener(Enum eventType,Object target,Method callback){
//注册事件,待会儿会用反射来调用方法
events.put(eventType,new Event(target,callback));
}
private void trigger(Event event){
event.setSource(this);
event.setTirggerTime(System.currentTimeMillis());
try {
//invoke的参数,第一个是object对象,第二个是方法
event.getCallback().invoke(event.getTarget(),event);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void trigger(Enum call){
if(!this.events.containsKey(call)){
return;
}
trigger(this.events.get(call).setTrigger(call.toString()));
}
}
Event
public class Event {
//事件源
private Object source;
//通知目标
private Object target;
//回调
private Method callback;
//触发器
private String trigger;
//时间
private long tirggerTime;
public Event(Object target, Method callback) {
this.target = target;
this.callback = callback;
}
public Object getSource() {
return source;
}
public Object getTarget() {
return target;
}
public Method getCallback() {
return callback;
}
public void setCallback(Method callback) {
this.callback = callback;
}
Event setSource(Object source) {
this.source = source;
return this;
}
protected void setTirggerTime(long tirggerTime) {
this.tirggerTime = tirggerTime;
}
Event setTrigger(String trigger) {
this.trigger = trigger;
return this;
}
public String getTrigger() {
return trigger;
}
public long getTirggerTime() {
return tirggerTime;
}
@Override
public String toString() {
return "Event{" +
"\t source=" + source +
"\t, target=" + target +
"\t, callback=" + callback +
"\t, trigger='" + trigger + '\'' +
'}';
}
}
MouseEventType——事件类型枚举
public enum MouseEventType {
ON_CLICK,
ON_DOUBLE_CLICK,
ON_UP,
ON_DOWN,
ON_WHEEL,
ON_MOVE,
ON_HOVER;
}
测试类:
public class MouseTest {
public static void main(String[] args) {
try {
//鼠标对象
Mouse mouse = new Mouse();
//鼠标事件的回调函数对象
MouseEventCallback callback = new MouseEventCallback();
//获得回调对象中的onClick事件处理函数
Method onClick = callback.getClass().getMethod("onClick",Event.class);
//加入监听绑定事件
System.out.println(onClick.toString());
mouse.addListener(MouseEventType.ON_CLICK,callback,onClick);
Method onDoubleClick = callback.getClass().getMethod("onDoubleClick",Event.class);
mouse.addListener(MouseEventType.ON_DOUBLE_CLICK,callback,onDoubleClick);
//人为调用鼠标事件
mouse.click();
mouse.doubleClick();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
说明:Mouse类继承了EventListener似乎就是表示鼠标类是一个消息发布者而已,EventListener的任务就是维护不同的事件类型,Event中的一些set方法为了简单,直接返回了this对象,这个有点不规范。EventListener中的一个trigger中是利用反射去调用的指定的响应函数,整个例子需要结合类图才能有更好的理解。
在一定程度上上述简单的实例好像也可以用动态代理来实现,确实可以,某一种层度上MouseCallback就是mouse的增强,这个理解也算是正确吧,但是这里也是为了说明观察者模式而举得一个常用的实例。