概述
发布订阅者模式其实在意图上等同于观察者模式,但是在结构上又有所不同。 在意图上,两者都是为对象创建一对多的关联关系,并且在对象发生改变之后通知其他对象。在结构上,观察者模式只有对象和观察者,发布订阅模式中存在 发布者、订阅者以及中间的调度中心。
使用意图
发布订阅者模式可以为对象创建一对多的关系,并在发布者发生改变时通知其订阅者。
使用场景
- 发布订阅者使用场景较为广泛,如今我们使用的消息中间件,如 Rocket MQ , Rabbit MQ等都使用到了发布订阅者模式。
- 在论坛、微博等社交网站中,也很常用的使用到了发布者订阅者模式。一个用户A可以订阅一个用户B,用户B发布了消息之后,通过调度中心C来选择该用户的订阅者进行推送(广播)消息。
与MVC模式之间的关系
在MVC模式下, 发布者和订阅者都属于Model,调度中心属于Service,订阅、取消订阅、发布信息都是应该在 Controller层完成。视图层则是用户操作页面。
逻辑方法展示
subscriber
↑
SubPub 1.提供订阅、2.取消订阅、3.发布信息等。
1.订阅时发生的事情: 添加 订阅者 id 与发布者 id 的关联信息
2.取消订阅时发生的事情: 删除订阅者 id 和 发布者 id 的关联信息
3.发布信息发生的事情: 查找 发布者 id 关联的 订阅者 id, 并找到订阅者,进行发送数据。
方法定义:
1.订阅(提供订阅者和发布者的id)
2.取消订阅(提供订阅者和发布者的id)
3.
3.1 发布信息(发布者id,发布的信息) --默认所有人可见
3.2 发布信息(发布者id,发布的信息,通知人列表) --- 其他的方法可以调用该方法完成
3.3 发布信息(发布者id,发布信息,权限类型[所有人可见、部分人可见、好友可见、仅自己可见]) =-=实际上会存在更多的权限类型。简单举例
↓
publisher
代码演示
- Publisher
/**
* @=-= 发布者。模型数据,只需要定义发布者所拥有的属性即可,操作交给控制器。
*/
public class Publisher {
private Integer id; //唯一。每个发布者的id都不相同
///Constructor And Getter\Setter
}
- Subscriber
/**
* @=-= 订阅者。 模型类,只负责定义数据,将操作转交给控制层。
*/
public class Subscriber {
private Integer id;
///Constructor And Getter\Setter
}
- Message
public class Message {
private String content;
///Constructor And Getter\Setter
}
- SubPubCentral
**
* @=-= 发布订阅中心。
*/
public interface SubPubCentral {
public boolean subscribe(Publisher publisher, Subscriber subscriber);
public boolean unsubscribe(Publisher publisher,Subscriber subscriber);
public void publish(Publisher publisher, Message message);
}
- SubPubCentralImpl
public class SubPubCentalImpl implements SubPubCentral {
private static Map<Integer, Set<Integer>> PubSubMap ; //存放所有的发布者的对应订阅者。
static{
PubSubMap = new HashMap<>();
}
@Override
public boolean subscribe(Publisher publisher, Subscriber subscriber) {
try{
Set<Integer> subscriberSet = PubSubMap.get(publisher.getId()); //拿到当前发布者的所有订阅者
if(subscriberSet == null) //为空,之前不存在订阅者
subscriberSet = new HashSet<>();
boolean added = subscriberSet.add(subscriber.getId()); //添加订阅者
if(added) //添加订阅者成功。
return PubSubMap.put(publisher.getId(),subscriberSet) != null;
return false; //订阅者添加失败或者该订阅之前则订阅了发布者
}catch(Exception e){
e.printStackTrace();
}
return false;
}
@Override
public boolean unsubscribe(Publisher publisher, Subscriber subscriber) {
try{
Set<Integer> subscriberSet = PubSubMap.get(publisher.getId());
if(subscriberSet == null)
return false;
boolean removed = subscriberSet.remove(subscriber.getId()); //删除取消订阅者
if(removed)
PubSubMap.put(publisher.getId(),subscriberSet); //更新订阅者列表
return removed;
}catch (Exception e){
e.printStackTrace();
}
return false;
}
@Override
public void publish(Publisher publisher, Message message) {
Set<Integer> subscriberSet = PubSubMap.get(publisher.getId());
//遍历订阅者发送消息。 此处简单实现: 只需要打印出拿到的所有订阅者即可
for (Integer subscriber:
subscriberSet) {
System.out.println("向发布者[" + publisher.getId()
+"]的订阅者[" + subscriber + "]发送消息: " + message);
}
}
}
- SubscriberController
/***
* @=-= 控制层,负责控制订阅者的一系列行为。
*/
public class SubscriberController {
private SubPubCentral subPubCentral;
public SubscriberController(SubPubCentral subPubCentral) {
this.subPubCentral = subPubCentral;
}
public void subscribe(Integer subscriberId,Integer publisherId){
subPubCentral.subscribe(new Publisher(publisherId),new Subscriber(subscriberId));
}
public void unsubscribe(Integer subscriberId,Integer publisherId){
subPubCentral.unsubscribe(new Publisher(publisherId),new Subscriber(subscriberId));
}
}
- PublisherController
/**
* @=-= 控制层。负责控制发布者的一系列行为。
*/
public class PublisherController {
private SubPubCentral subPubCentral; //订阅发布中心。
public PublisherController(SubPubCentral subPubCentral) {
this.subPubCentral = subPubCentral;
}
/**
* @=-= 发布数据,假设前端传递的是一个id对象和一个内容对象。简单化,实际上会传递一个包装数据过来.
* @param publisherId
* @param message
*/
public void publish(Integer publisherId,String message){
subPubCentral.publish(new Publisher(publisherId),new Message(message));
}
}
- Main
package cn.tblack.observable;
public class Main {
/**
* @=-= 测试发布订阅。模拟事件。
* @param args
*/
public static void main(String[] args) {
SubPubCentral subPubCentral = new SubPubCentalImpl();
PublisherController publisherController = new PublisherController(subPubCentral);
SubscriberController subscriberController = new SubscriberController(subPubCentral);
subscriberController.subscribe(1,1);
subscriberController.subscribe(2,1);
publisherController.publish(1,"我今天很好");
System.out.println("\n----------------------------------------------------\n\n");
subscriberController.unsubscribe(1,1);
publisherController.publish(1,"我过得很好");
}
}
执行结果: