观察者模式、发布订阅和事件驱动
观察者模式(有时又被称为模型(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的类的实例就可以收到消息,当然程序的复杂性就增加了。
最后总结一下,观察者模式和发布订阅模型的区别就在于消息是否发送给中间件,观察者和被观察着是否完全解耦;而观察者模式和事件驱动的区别则在于事件驱动则更加灵活,但同时增加了程序的复杂性。