Java中使用发布订阅模式

概述

发布订阅者模式其实在意图上等同于观察者模式,但是在结构上又有所不同。 在意图上,两者都是为对象创建一对多的关联关系,并且在对象发生改变之后通知其他对象。在结构上,观察者模式只有对象和观察者,发布订阅模式中存在 发布者、订阅者以及中间的调度中心。

使用意图

发布订阅者模式可以为对象创建一对多的关系,并在发布者发生改变时通知其订阅者。

使用场景

  • 发布订阅者使用场景较为广泛,如今我们使用的消息中间件,如 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,"我过得很好");

    }
}

执行结果:
在这里插入图片描述

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

David_TD

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

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

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

打赏作者

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

抵扣说明:

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

余额充值