前言
观察者模式有另一个名字,叫发布订阅模式,之前的总结有点过于简单,理解的并不深入,这里重新梳理一个实例,重新进行总结。之前的理解:观察者模式
实例
实际开发工作中,写代码算是核心工作,但是让我们心里最不安的,依旧是半夜的系统报警。前几天思考观察者设计模式,发现其实这个实例貌似就和我们处理半夜的线上系统异常类似,这里就以这个场景来说明一下观察者模式。
半夜修复线上bug的小李,为观察者。线上运行的系统可以比喻成被观察者。先给出这两个实例代码。
1、线上运行的系统(被观察者)
public class ServerOnline {
public void runNormal(){
System.out.println("服务器正常运行");
}
public void runException(){
System.out.println("服务器运行出现异常");
}
}
2、程序员小李(观察者)
public class CoderLi {
public void problemMessage(){
System.out.println("程序出现了线上问题,请及时处理");
}
}
现在被观察者和观察者都有了 ,那么问题来了。1、出现问题了如何通知小李?2、小李收到的通知需要包含一些具体的内容,但是上面的Message方法中,貌似小李得不到任何信息,自然小李也无法处理问题。
针对问题一,无非就是在runException的时候调用CoderLi中的problemMessage方法即可。针对问题二,我们需要封装先关的消息作为参数,传递给problemMessage中,为此我们需要引入一个类——Message
3、Message实体
package com.learn.designModel.ObserverPattern.OnlineProblem;
import java.lang.reflect.Method;
/**
* autor:liman
* comment: 被观察者与观察者之间传递的消息实体
*/
public class Message {
private Object source; //事件源,被观察者
private Object target; //回调的目标对象,观察者
private String event; //事件类型
private Method callback; //回调的处理方法,通常是target对象中的某个方法
private Long occurTime;
public Message(Object target, Method callback) {
this.target = target;
this.callback = callback;
}
//这里省略getter/setter方法
@Override
public String toString() {
return "Message{" +"\n"+
"source=" + source +"\n"+
", target=" + target +"\n"+
", event='" + event + '\'' +"\n"+
", callback=" + callback +"\n"+
", occurTime=" + occurTime +"\n"+
'}';
}
}
程序员小李作出如下修改:
public class CoderLi {
public void problemMessage(Message message){
System.out.println("程序出现了线上问题,请及时处理");
System.out.println("异常信息为:"+message);
}
}
现在问题又来了,线上有问题了,如何自动通知程序员小李呢?这里就引入了MessageListener这个类。
4、MessageListener类
package com.learn.designModel.ObserverPattern.OnlineProblem;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* autor:liman
* comment:用于关联事件类型与消息体
*/
public class MessageListener {
private Map<String,Message> eventToMessage = new HashMap<>();
public void addListener(String messageType,Object target,Method callback){
eventToMessage.put(messageType,new Message(target,callback));
}
/**
* 内部调用的trigger
* @param message
*/
public void trigger(Message message){
//因为子类会继承这个类,子类就是被观察者,所以这里赋值为this
message.setSource(this);
message.setOccurTime(System.currentTimeMillis());
try {
message.getCallback().invoke(message.getTarget(),message);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param messageType 消息类型
*/
public void trigger(String messageType){
if(!eventToMessage.containsKey(messageType)){
return;
}
Message message = eventToMessage.get(messageType);
message.setEvent(messageType);
trigger(message);
}
}
那么问题依旧:被观察者如何自动通知观察者呢?也就是程序线上出了问题,如何自动通知程序员小李呢?,为了让被观察者具有这个功能,我们通常需要让观察者继承MessageListener这个类,所以被观察者需要作如下修改:
package com.learn.designModel.ObserverPattern.OnlineProblem;
/**
* autor:liman
* comment: 服务器,被观察者
*/
public class ServerOnline extends MessageListener{
public void runNormal(){
System.out.println("服务器正常运行");
}
public void runException(){
System.out.println("服务器运行出现异常");
System.out.println("=================开始进入告警机制================");
//出了问题,通知观察者(程序员小李)
this.trigger("ERROR");
}
}
测试实例:
package com.learn.designModel.ObserverPattern.OnlineProblem;
/**
* autor:liman
* comment:
*/
public class OnlineProblemTest {
public static void main(String[] args) throws NoSuchMethodException {
CoderLi coderLi = new CoderLi();
ServerOnline serverOnline = new ServerOnline();
serverOnline.addListener("ERROR",coderLi,
coderLi.getClass().getMethod("problemMessage",new Class<?>[]{Message.class}));
serverOnline.runException();
}
}
运行结果:
一些说明:
其实针对上述实例,如果详细类比一下会发现,在之前窗体应用开发中,Message就好比Event实例,ServerOnLine就好比指定的控件,而控件有各种事件,就好比“ERROR” 上述实例中只涉及到了一个事件类型,用固定写成了ERROR,如果稍微复杂一点,可以用枚举。
其实观察者设计模式最大的好处就是解耦,在ServerOnline中,我不需要知道我通知的是谁,只需要触发指定的ERROR事件类型即可。自然就会有程序员小李来处理线上问题,所以这就是解耦的最好体现,在实际应用中,消息队列的解耦思想与这个如出一辙。
JDK的实现
其实观察者模式JDK其实已经为我们提供了实现方式。这里也通过一个简单实例来说明这个问题。就以百度知道上的提问模式来解释JDK的观察者模式
1、提问平台(百度知道)被观察者,需要继承Observable
/**
* autor:liman
* comment:
*/
public class Forum extends Observable {
private String name = "百度知道";
private static Forum forum = null;
private Forum(){}
public static Forum getForum(){
if(forum == null){
forum=new Forum();
}
return forum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 发布问题
*/
public void publicQuestion(Question question){
System.out.println(question.getUserName()+":发布了一个问题,"+question.getContent()+"!");
//设置变化
setChanged();
notifyObservers(question);
}
}
2、 解答问题的老师(观察者) 需要实现Observer接口,这个接口中有一个update方法,这个方法类似消息队列使用时候的onMessage方法
/**
* autor:liman
* comment:
*/
public class Teacher implements Observer {
private String name;
public Teacher(String name){this.name = name;}
@Override
public void update(Observable o, Object arg) {
Forum forum = (Forum)o;
Question question = (Question)arg;
System.out.println("================================");
System.out.println("您收到了一个来自百度知道的消息:"+question.getUserName()+" 的提问:"+question.getContent());
}
}
3、问题(类似于消息对象)
/**
* autor:liman
* comment:
*/
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;
}
}
测试实例
/**
* autor:liman
* comment:
*/
public class JDKObserverTest {
public static void main(String[] args) {
Teacher teacher = new Teacher("liman");
Forum forum = Forum.getForum();
Question question = new Question();
question.setContent("你最喜欢的人是谁?");
question.setUserName("test");
forum.addObserver(teacher);
forum.publicQuestion(question);
}
}
运行结果:
总结
观察者模式同时也是spring中使用的较多的一种设计模式,其优点就在于解耦,之前无法理解监听器与事件源的关系,这篇博客中算是给出了较好的总结。