观察者模式发布订阅和事件驱动

Posted hhhshct

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察者模式发布订阅和事件驱动相关的知识,希望对你有一定的参考价值。

  观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

  观察者模式(Observer)完美的将观察者和被观察的对象分离开,以明星和粉丝举例子,明星就是被观察着,粉丝就是观察者。下面是一个demo

package Observer;

public interface Observer {

    void update(Observable o);
}
package Observer;

import java.util.ArrayList;
import java.util.List;

public class Observable {

    private List<Observer> list = new ArrayList<>();
    public void addObserver(Observer o) {
        list.add(o);
    }
    private String status;
    
    public List<Observer> getList() {
        return list;
    }

    public String getStatus() {
        return status;
    }

    public void movelUp (String status) {
        System.out.println(status);
        this.status = status;
        list.stream().forEach(o -> o.update(this));
    }
    
}
package Observer;

public class Client {

    public static void main(String[] args) {
        Observable ob = new Observable();
        ob.addObserver(new ObserverImpl("小花"));
        ob.addObserver(new ObserverImpl("小名"));
        ob.movelUp("天下无贼");
    }
}

  以上便是标准的观察者模式,在被观察着中维护了一个观察者的列表,需要由被观察者去完成注册观察者的功能,不符合单一职责原则。所以我们需要添加一个manager类,来方便用户自己去订阅某个明星。改造代码如下:

package Observer2;

import java.util.ArrayList;
import java.util.List;

public class Star {

    private String name;

    private String lastMovel;

    // 标识被观察者是否变化
    private boolean changed = false;

    private List<Observer> list = new ArrayList<>();

    public void addObserver(Observer o) {
        list.add(o);
    }

    public List<Observer> getList() {
        return list;
    }

    public void notifyObserver() {
        if (!changed) {
            return;
        }
        setChanged(false);
        list.stream().forEach(o -> o.update(this, null));
    }

    public boolean isChanged() {
        return changed;
    }

    public void setChanged(boolean changed) {
        this.changed = changed;
    }

    public void setList(List<Observer> list) {
        this.list = list;
    }

    public Star(String name) {
        super();
        this.name = name;
        Manager.getInstance().addStar(this);
    }

    
    public void movelUp(String movel) {
        System.out.println(name + "发布了" + movel);
        this.lastMovel = movel;
        setChanged(true);
        notifyObserver();
    }

    public String getName() {
        return name;
    }

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

    public String getLastMovel() {
        return lastMovel;
    }

    public void setLastMovel(String lastMovel) {
        this.lastMovel = lastMovel;
    }

}
package Observer2;

public interface Observer {

    void update(Star o,Object args);
}
package Observer2;

public class Fans implements Observer {
    
    private String name;
    
    

    public Fans(String name) {
        super();
        this.name = name;
    }



    public String getName() {
        return name;
    }



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


    public void subscribe(String starName) {
        Manager.getInstance().getStar(starName).addObserver(this);
    }

    @Override
    public void update(Star star,Object args) {
        System.out.println(name + "得到了" + star.getName() +"发布" + star.getLastMovel() + "的消息");
    }

    
}
package Observer2;

import java.util.HashMap;
import java.util.Map;

public class Manager {

    private Map<String, Star> observableMap = new HashMap<>();
    
    private Manager() {
        
    }
    public void addStar(Star star) {
        observableMap.put(star.getName(), star);
    }
    public Star getStar(String name) {
        return observableMap.get(name);
    }
    public static Manager getInstance() {
        return SingleManager.manager;
    }
    private static class SingleManager{
        private static Manager manager = new Manager();
        
    }
    
    
}
package Observer2;

public class Client {

    public static void main(String[] args) {
        Fans fans1 = new Fans("小明");
        Fans fans2 = new Fans("小话");
        Fans fans3 = new Fans("小张");
        Fans fans4 = new Fans("小里");
        Star star1 = new Star("丽影");
        Star star2 = new Star("幂幂");
        fans1.subscribe("丽影");
        fans3.subscribe("幂幂");
        fans4.subscribe("幂幂");
        star1.movelUp("乘风破浪");
        star2.movelUp("孤岛惊魂");
        System.out.println("----------------------------");
        fans2.subscribe("丽影");
        star1.movelUp("女儿国");
    }
}

  发布/订阅模式(Pub/Sub)是一种消息模式,它有 两个参与者 :  发布者和订阅者 。发布者向 某个信道发布一条消息,订阅者绑定这个信道,当有消息发布至信道时就会 接收到一个通知。最重要的一点是, 发布者和订阅者是完全解耦的,彼此并不知晓对方的存在。从定义上可以看出,发布订阅模式里双方是完全解耦的,而在观察者模式里,目标对象管理这观察者,双方是耦合的,这是最主要的区别,而在发布订阅模式中多了一个中间层信道。我们常用的activeMQ、rabbitMQ、kafka等中间件就是为发布订阅模式服务的。

  还有一种和观察者模式很像的就是事件驱动模型,相信各位都知道tomcat,在使用的过程中,或许经常会有人用到listener,即监听器这个概念。那么其实这个就是一个事件驱动模型的应用。比如我们的spring,我们在应用启动的时候要初始化我们的IOC容器,那么我们的做法就是加入一个listener,这样伴随着tomcat服务器的启动,spring的IOC容器就会跟着启动。那么这个listener其实就是事件驱动模型中的监听器,它用来监听它所感兴趣的事,比如我们springIOC容器启动的监听器,就是实现的ServletContextListener这个接口,说明它对servletContext感兴趣,会监听servletContext的启动和销毁。事件驱动模型与观察者模式勉强的对应关系可以看成是,被观察者相当于事件源,观察者相当于监听器,事件源会产生事件,监听器监听事件。所以这其中就搀和到四个类,事件源,事件,监听器以及具体的监听器。下面我们就将上面的例子改造为事件驱动模型。

package Observer3;

import java.util.EventObject;

public class MovelEvent extends EventObject {

    private static final long serialVersionUID = -1231609728871248531L;

    public MovelEvent(Star star) {
        super(star);
    }

    public Star getStar() {
        return (Star) super.getSource();
    }
}
package Observer3;

import java.util.ArrayList;
import java.util.List;

public class Star {
    // 标识被观察者是否变化
    private boolean changed = false;

    private List<MovelListener> list = new ArrayList<>();

    public void addMovelListener(MovelListener o) {
        list.add(o);
    }

    public List<MovelListener> getList() {
        return list;
    }

    public void setList(List<MovelListener> list) {
        this.list = list;
    }

    public void notifyObserver() {
        if (!changed) {
            return;
        }
        setChanged(false);
        MovelEvent movelEvent = new MovelEvent(this);
        list.stream().forEach(o -> o.update(movelEvent));
    }

    public boolean isChanged() {
        return changed;
    }

    public void setChanged(boolean changed) {
        this.changed = changed;
    }


    public Star(String name) {
        super();
        this.name = name;
        Manager.getInstance().addStar(this);
    }

    private String name;

    private String lastMovel;

    public void movelUp(String movel) {
        System.out.println(name + "发布了" + movel);
        this.lastMovel = movel;
        setChanged(true);
        notifyObserver();
    }

    public String getName() {
        return name;
    }

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

    public String getLastMovel() {
        return lastMovel;
    }

    public void setLastMovel(String lastMovel) {
        this.lastMovel = lastMovel;
    }

}
package Observer3;

import java.util.EventListener;

public interface MovelListener extends EventListener {
    
    void update (MovelEvent movelEvent);

}
package Observer3;

public class Fans implements MovelListener {
    
    private String name;
    
    

    public Fans(String name) {
        super();
        this.name = name;
    }



    public String getName() {
        return name;
    }



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


    public void subscribe(String starName) {
        Manager.getInstance().getStar(starName).addMovelListener(this);
    }

    @Override
    public void update(MovelEvent movelEvent) {
        Star star = movelEvent.getStar();
        System.out.println(name + "得到了" + star.getName() +"发布" + star.getLastMovel() + "的消息");
        
    }

    
}

   我们彻底将刚才的观察者模式改成了事件驱动,现在我们使用事件驱动的类再运行一下客户端,其中客户端代码和WriterManager类的代码是完全不需要改动的,直接运行客户端即可。我们会发现得到的结果与观察者模式一模一样。

  那么观察者模式和事件驱动的不同之处在哪里呢?首先从实现方式上就能看出,事件驱动可以解决观察者模式的问题,但反过来则不一定,另外二者所表达的业务场景也不一样,比如上述例子,使用观察者模式更贴近业务场景的描述,而使用事件驱动,从业务上讲,则有点勉强。再者从功能上讲,观察者模式中观察者的响应理论上讲针对特定的被观察者是唯一的(当然如果在update方法中ifelse则也能实现对多个被观察着);事件驱动则更灵活,可以定义自己感兴趣的事情,比如我们可以监听movel,也可以在接口里加一个singListener的接口,然后我们的fans同时实现这个接口,那么star在发布singEvent的时候,只要实现了singListener的类的实例就可以收到消息,当然程序的复杂性就增加了。

  最后总结一下,观察者模式和发布订阅模型的区别就在于消息是否发送给中间件,观察者和被观察着是否完全解耦;而观察者模式和事件驱动的区别则在于事件驱动则更加灵活,但同时增加了程序的复杂性。

  

以上是关于观察者模式发布订阅和事件驱动的主要内容,如果未能解决你的问题,请参考以下文章

事件驱动的简明讲解

观察者模式事件驱动模式一些思考

设计模式3 - 观察者模式 Observer Pattern

spring事件驱动模型--观察者模式在spring中的应用

C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命

发布订阅模式实现及发布订阅者模式与观察者模式的不同