观察者模式和发布订阅模式

一、概念

观察者(Observer),又称发布-订阅(Publish-Subscrice),属于23中设计模式之一。 发布订阅模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态。

二、应用场景

以租房为例,小明到中介租房,但是合适的房源已经没有了,所以小明就会留下自己的联系方式给中介,一旦有合适的房源,中介就会通知小明。而中介对接向小明一样需求的客户有很多个,中介会通知他对接的所有顾客,这就是一个发布订阅的案例。其中小明就是订阅者,中介就是发布者,小明在中介里进行订阅之后,中介在找到符合房源后会进行发布,让订阅者收到消息。
类似这种就是发布订阅的案例,生活中的公众号、博客关注博主等等都是发布订阅的应用。

三、观察者模式和发布订阅模式的关系与区别

1、观察者设计模式:

观察者模式: 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
观察者模式的别名包括发布-订阅模式、模型-视图等。
细究的话,发布订阅和观察者有些不同,可以理解成发布订阅模式属于广义上的观察者模式。
发布-订阅设计模式

2、发布订阅模式

在发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。
意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做订阅中心,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。

所以观察者模式和发布订阅者模式其实是大同小异,原理都是一样的,区别就在于有没有订阅中心。在GOF23中设计模式中是只有观察者模式,发布订阅模式是不存在的。有些人认为观察者和发布订阅是同一种模式,而有些人认为这是两种设计模式,关键看你怎么理解。
在这里插入图片描述

四、代码实现

1、有订阅中心

订阅者实体类Subscriber

public class Subscriber {
    private String name;

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

    public String getName() {
        return name;
    }
}

发布者实体类Publisher

public class Publisher {
    private String name;  //发布者的名字

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

    public String getName() {
        return name;
    }
}

发布订阅中心SubPubCentral,发布订阅中心需要实现该接口

public interface SubPubCentral {

    /**
     * 添加发布者和订阅者
     * @param publisher
     * @param subscriber
     * @return
     */
    public boolean subscribe(Publisher publisher, Subscriber subscriber);

    /**
     * 取消发布者和订阅者
     * @param publisher
     * @param subscriber
     * @return
     */
    public boolean unsubscribe(Publisher publisher, Subscriber subscriber);

    /**
     * 发布消息
     * @param publisher
     * @param message
     */
    public void publish(Publisher publisher, String message);
}

发布订阅中心实现类SubPubCentralImpl

public class SubPubCentralImpl implements SubPubCentral {

    private static Map<String, Set<String>> PubSubMap = new HashMap<>();  //存放所有的发布者的对应订阅者

    @Override
    public boolean subscribe(Publisher publisher, Subscriber subscriber) {
        try {
            Set<String> subscriberSet = PubSubMap.get(publisher.getName()); //拿到当前发布者的所有订阅者
            if (subscriberSet == null)  //为空,之前不存在订阅者
                subscriberSet = new HashSet<>();
            boolean added = subscriberSet.add(subscriber.getName()); //添加订阅者
            if (added)  //添加订阅者成功。
                return PubSubMap.put(publisher.getName(), subscriberSet) != null;
            return false;  //订阅者添加失败或者该订阅之前则订阅了发布者
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean unsubscribe(Publisher publisher, Subscriber subscriber) {
        try {
            Set<String> subscriberSet = PubSubMap.get(publisher.getName());
            if (subscriberSet == null)
                return false;
            boolean removed = subscriberSet.remove(subscriber.getName());       //删除取消订阅者
            if (removed)
                PubSubMap.put(publisher.getName(), subscriberSet); //更新订阅者列表
            return removed;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void publish(Publisher publisher, String message) {
        Set<String> subscriberSet = PubSubMap.get(publisher.getName());//拿到当前发布者的所有订阅者

        //遍历订阅者发送消息。 此处简单实现: 只需要打印出拿到的所有订阅者即可
        for (String subscriber : subscriberSet) {
            System.out.println(subscriber + "接收到" + publisher.getName() + "发布的新消息:" + message);
        }
    }
}

控制层,负责控制发布者的一系列行为

public class PublisherController {
    private SubPubCentral subPubCentral;  //订阅发布中心。

    public PublisherController(SubPubCentral subPubCentral) {
        this.subPubCentral = subPubCentral;
    }

    /**
     * 发布数据,假设前端传递的是一个id对象和一个内容对象。简单化,实际上会传递一个包装数据过来.
     *
     * @param publisher
     * @param message
     */
    public void publish(Publisher publisher, String message) {
        subPubCentral.publish(publisher, message);
    }
}

控制层,负责控制订阅者的一系列行为

public class SubscriberController {

    private SubPubCentral subPubCentral;

    public SubscriberController(SubPubCentral subPubCentral) {
        this.subPubCentral = subPubCentral;
    }

    /**
     * 订阅者与订阅发布者
     *
     * @param publisher
     * @param subscriber
     */
    public void subscribe(Publisher publisher, Subscriber subscriber) {
        subPubCentral.subscribe(publisher, subscriber);
    }

    public void unsubscribe(Publisher publisher, Subscriber subscriber) {
        subPubCentral.unsubscribe(publisher, subscriber);
    }
}

模拟发布订阅

public class Test {
    public static void main(String[] args) {
        //创建一个发布订阅中心
        SubPubCentral subPubCentral = new SubPubCentralImpl();
    	//创建发布者和订阅者的控制层
        PublisherController publisherController = new PublisherController(subPubCentral);
        SubscriberController subscriberController = new SubscriberController(subPubCentral);
    	//创建订阅者对象
        Subscriber subscriber1=new Subscriber("小明");
        Subscriber subscriber2=new Subscriber("小李");
    	//创建发布者对象
        Publisher publisher=new Publisher("中介");
        //订阅者订阅
        subscriberController.subscribe(publisher, subscriber1);
        subscriberController.subscribe(publisher, subscriber2);
    	//发布者发布新消息
        publisherController.publish(publisher, "天河区有新房出租!");

        System.out.println("\n----------------------------------------------------\n");
        //订阅者取消订阅
        subscriberController.unsubscribe(publisher, subscriber1);
        //发布者发布新消息
        publisherController.publish(publisher, "番禺区有新房出租!");
    }
}

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

订阅者可以调用subscribe方法订阅对象和调用unsubscribe方法取消订阅对象,而发布者可以调用publish方法发布新消息给所有订阅他的对象。而不管是建立/取消订阅关系还是发布新消息都是有发布订阅中心PublisherController统一调用实现,就像现实中租房的中介,不管是租客还是房主,都是通过中介进行调度、通知。

2、观察者实现代码(无订阅中心)

主题Subject

public interface Subject {

    /**
     * 添加观察者
     *
     * @param concreteObserver
     */
    public void attach(ConcreteObserver concreteObserver);


    /**
     * 发布消息
     *
     * @param msg
     */
    public void notify(String msg);

    /**
     * 移除观察者
     *
     * @param concreteObserver
     */
    public void detach(ConcreteObserver concreteObserver);
}

观察者接口

public interface Observer {
    /**
     * 订阅内容
     * @param msg
     */
    public void update(String subject, String msg);
}

主题实现类ConcreteSubject

public class ConcreteSubject implements Subject {
    //定义一个集合来储存观察者
    private ArrayList<ConcreteObserver> events = new ArrayList<>();

    //订阅的主题名字
    String name;

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

    @Override
    public void attach(ConcreteObserver concreteObserver) {
        events.add(concreteObserver);
    }

    @Override
    public void notify(String msg) {
        for (int i = 0; i < events.size(); i++) {
            ConcreteObserver concreteObserver = events.get(i);
            concreteObserver.update(this.name, msg);
        }
    }

    @Override
    public void detach(ConcreteObserver concreteObserver) {
        events.remove(concreteObserver);
    }
}

观察者实例

public class ConcreteObserver implements Observer {
    private String name;
    private Receiver receiver;

    public interface Receiver {
        void onMessage(String publisher, String msg);
    }

    //创建观察者
    public ConcreteObserver(String name) {
        this.name = name;
    }

    //创建观察者也可以加个回调函数,用来将消息调出去
    public ConcreteObserver(String name, Receiver receiver) {
        this.name = name;
        this.receiver = receiver;
    }

    @Override
    public void update(String subject, String msg) {
        System.out.println(this.name + "收到" + subject + "发送的消息:" + msg);
        if (receiver != null) {
            receiver.onMessage(subject, msg);
        }
    }
}

模拟实现

public class Test {
    public static void main(String[] args) {
        //创建观察者
        ConcreteObserver concreteObserver1 = new ConcreteObserver("小明", new ConcreteObserver.Receiver() {
            @Override
            public void onMessage(String publisher, String msg) {
                //收到消息
                System.out.println(publisher + "说" + msg);
            }
        });
        ConcreteObserver concreteObserver2 = new ConcreteObserver("小华");
        ConcreteObserver concreteObserver3 = new ConcreteObserver("小红");


        //创建主题
        ConcreteSubject concreteSubject = new ConcreteSubject("BOSS");
        concreteSubject.attach(concreteObserver1);
        concreteSubject.attach(concreteObserver2);
        concreteSubject.attach(concreteObserver3);

        concreteSubject.notify("今天放假!");
    }
}

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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
转载请注明出处,此系列的例子都是本人想了很久认为适合教学容易理解的,若有更好的例子也请发邮件给我一起研究,nj.lg#163.com Observer (观察者模式) 又叫做发布/订阅(Publish/Subscribe)模式。 当一个对象的改变同时会影响其他对象的行为的时候,可以使用此设计模式。 l 主题对象 :一个需要被关注的主题对象,这个主题对象改变会影响其他对象的行为 l 订阅对象:当主题对象发生改变,订阅对象需要相应处理。 l 观察者对象:抽象出观察者对象,负责管理需要被通知的订阅对象并通知它们。 当主题对象发生改变时如何通知观察者对象 l 主题对象内部包含观察者对象,发生改变时主动去通知观察者对象 l 启动一个线程轮询查看主题对象,发现改变主动去通知观察者对象 l 例子 便于大家理解举个例子并实现。代码为Java 代码,使用了Spring和JUnit。 l 需求 有一个小朋友叫小明。 妈妈每年要给他办生日派对, 爸爸关注小明的学习,到了年龄就要给小明找学校,从幼儿园,小学,初中,高中,大学。 小明的爷爷是个老财主,等小明大学毕业了就送小明一套别墅。 l 分析 n 有一个人小明 作为被关注的主题对象(关注的是小明的年龄) n 三个订阅对象 u 妈妈:每年都要给小明过生日 u 爸爸:根据小明的年龄情况需要给小明找学校 u 爷爷:等小明大学毕业送别墅给小明 抽象出一个观察者对象,负责通知和管理三个订阅对象

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值