设计模式-观察者模式

观察者模式的应用场景
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。观察者模式主要用于在关联行为之间建立一套触发机制的场景。观察者模式在现实生活应用也非常广泛,比如:微信朋友圈动态通知、GPser 生态圈消息通知、邮件通知、广播通知、桌面程序的事件响应等(如下图)。
在这里插入图片描述

MQ,异步队列等都是观察者模式的一种实现,JDK 本身就提供这样的API
这里我们以学生在论坛问题为例,学生在网站提问后,老师可以收到学生的提示 用jdkAPI实现:
学生类 继承Observable:

public class Student extends Observable {
    private String name ;

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

    public void publishQue(Question ques){
        //发布完问题就去通知所有监听者
        System.out.println("发布问题:"+ques.getContent());
        setChanged();
        notifyObservers(ques);
    }

    public String getName() {
        return name;
    }

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

问题类:

public class Question {
    private String  userName;
    private String  content;

    public Question(String userName, String content) {
        this.userName = userName;
        this.content = 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;
    }
}

老师类 实现 Observer :

public class Teacher implements Observer {
    private String name ;

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

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

    @Override
    public void update(Observable o, Object arg) {
        Student stu=(Student) o;
        Question que= (Question) arg;
        System.out.println("老师 "+this.name+" 收到了 "+((Student) o).getName()+" 的问题:"+((Question) arg).getContent());
    }
}

测试类

public class ObserverTest {
    public static void main(String[] args) {
        Student stu= new Student("小张");
        Question que= new Question(stu.getName(),"地球为什么是圆的?");
        Teacher zhang = new Teacher("zhang");
        Teacher wang = new Teacher("wang");
        stu.addObserver(zhang);
        stu.addObserver(wang);
        stu.publishQue(que);
    }
}

运行结果,当发布问题后,老师都收到了通知:
在这里插入图片描述

JDK 源码中,观察者模式也应用非常多。例如java.awt.Event 就是观察者模式的一种,只不过Java 很
少被用来写桌面程序。我们自己用代码来实现一下,感受一下原理
Event类:

//定义谁在什么时候触发什么事件  回调需要调用的target的哪个目标方法
public class Event {
    private Object source;
    private Object target;
    private Date time;
    private String trigger ;
    private Method callback;

    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 Date getTime() {
        return time;
    }

    public void setTime(Date time) {
        this.time = time;
    }

    public String getTrigger() {
        return trigger;
    }

    public void setTrigger(String trigger) {
        this.trigger = trigger;
    }

    public Method getCallback() {
        return callback;
    }

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

ButtonEventType 定义事件类型

//定义事件类型
public interface ButtonEventType {
    //单击
    String ON_CLICK = "click";
    //双击
    String ON_DOUBLE_CLICK = "doubleClick";
    //弹起
    String ON_UP = "up";
}

定义按钮事件回调接口ButtonEventCallback

public interface ButtonEventCallback {
    public void onClick(Event e);
    public void onDoubleClick(Event e);
    public void onUp(Event e);
}

监听实现的关键 EventLisenter ,实现了添加监听的操作,只要组件继承了这个类 就可以进行监听操作了。

//监听器 观察者和被观察者的桥梁
public class EventLisenter {
    protected Map<String,Event> events = new HashMap<String,Event>();
    public void addListener(String eventType,Object target){
        try{
            this.addListener(eventType,target,target.getClass().getMethod("on"+toUpperFirstCase(eventType),Event.class));
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void addListener(String eventType, Object target, Method method) {
        events.put(eventType,new Event(target,method));
    }

    private void trigger(Event event){
        event.setSource(this);
        event.setTime(new Date());
        try{
            if(event.getCallback()!=null){
                event.getCallback().invoke(event.getTarget(),event);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //触发事件
    protected void trigger(String trigger){
        if(this.events.containsKey(trigger)){
            trigger(this.events.get(trigger));
        }
    }

    private String toUpperFirstCase(String str){
        char[] chars = str.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

}

定义 按钮类,

public class Button extends EventLisenter {
    public void click(){
        System.out.println("调用单击方法");
        this.trigger(ButtonEventType.ON_CLICK);
    }
    public void doubleClick(){
        System.out.println("调用双击方法");
        this.trigger(ButtonEventType.ON_DOUBLE_CLICK);
    }
    public void up(){
        System.out.println("调用弹起方法");
        this.trigger(ButtonEventType.ON_UP);
    }


    public static void main(String[] args) {
        Button m= new Button();
        m.addListener(ButtonEventType.ON_CLICK, new ButtonEventCallback() {
            @Override
            public void onClick(Event e) {//onClick
                System.out.println("按钮被点击");
            }
            @Override
            public void onDoubleClick(Event e) {
            }
            @Override
            public void onUp(Event e) {
            }
        });

        m.click();
    }

执行测试代码,按钮click 被调用后 我们的回调代码也执行了。结果如下:
在这里插入图片描述

实现原理分析:定义好事件类型、回调接口。然后需要被监听的组件中增加监听支持逻辑,用一个集合保存监听器,当组件的方法被执行以后 到集合中查看是否有该方法的监听器,如果有则调用监听器的回调方法。

观察者模式在源码中的应用
Spring 中的ContextLoaderListener 实现了ServletContextListener 接口,ServletContextListener 接口又继承了EventListener,在JDK 中EventListener 有非常广泛的应用。我们可以看一下源代码,ContextLoaderListener:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
public ContextLoaderListener(WebApplicationContext context) {
    super(context);
}
public void contextInitialized(ServletContextEvent event) {
    this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
    this.closeWebApplicationContext(event.getServletContext());
    ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}

ServletContextListener

public interface ServletContextListener extends EventListener {
    public void contextInitialized(ServletContextEvent sce);
    public void contextDestroyed(ServletContextEvent sce);
}

EventListener

public interface EventListener {
}

基于Guava API 轻松落地观察者模式
在这里,推荐给大家一个实现观察者模式非常好用的框架。API 使用也非常简单,举个例子,先引入maven 依赖包:

<dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>20.0</version>
    </dependency>

测试类代码:

public class GuavaEvent {
    @Subscribe
   public void subscribe(String str){
      //业务逻辑
      System.out.println("执行subscribe 方法,传入的参数是:" + str);
   }

    public static void main(String[] args) {
        EventBus eb= new EventBus(  );
        GuavaEvent ge= new GuavaEvent();
        eb.register(ge);
        eb.post("test");
    }
}

观察者模式的优缺点
优点:
1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信。
缺点:
1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
2、使用要得当,要避免循环调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catch that elf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值