观察者模式(Observer)

发布时间 2023-03-30 23:10:31作者: primaryC

1,观察者模式

  观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。

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

2,观察者模式角色

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

3,观察者模式 Demo

1,抽象观察者

package com.demo.base.design_partten.observer;

/**
 * 观察者
 * */
public interface Observer {
    /*
    * 订阅内容,接收订阅内容
    * */
    public void attachMsg(String subject, String msg);

}

2,抽象主题

package com.demo.base.design_partten.observer;

/**
 * 主题
 * @author admin
 * */
public interface Subject {

    /**
     * 添加观察者
     * */
    public void attach(Observer observer);

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

    /**
     * 移除观察者
     * */
    public void detach(Observer observer);
}

3,观察者实现

package com.demo.base.design_partten.observer;

/**
 * 观察者实例
 * */
public class ConcreteObserver implements Observer {

    String name;

    @Override
    public void attachMsg(String subject, String msg) {
        System.out.println(String.format("观察者:%s 收到 %s 发布的消息:%s", name, subject, msg));

        //doSth...
    }

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

4,主题实现

package com.demo.base.design_partten.observer;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * 主题实现类
 * */
public class ConcreteSubject implements Subject{

    /**
     * 观察者集合
     * */
    private Set<Observer> events = new LinkedHashSet<>();

    //主题名称
    String name;

    @Override
    public void attach(Observer observer) {
        events.add(observer);
    }

    @Override
    public void notify(String msg) {
        for(Observer observer : events){
            observer.attachMsg(name, msg);
        }
    }

    @Override
    public void detach(Observer observe) {
        events.remove(observe);
    }

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

5,业务类

package com.demo.base.design_partten.observer;

/**
 * 观察者模式:
 * 1,观察者接口
 *    定义订阅方法,用来接收发布的消息
 * 2,观察者实现类
 *    实现订阅方法,接收发布的消息
 * 3,主题接口 subject
 *    定义了添加观察者,删除观察者,发布消息三个接口
 * 4,主题实现类 ConcreteSubject
 *    内部持有一个观察者结合,实现发布消息接口,发布消息时候循环调用观察者订阅方法。
 * 5,测试方法
 *    观察者订阅主题,调用主题的 attach 方法。
 *    主题发布消息,会通知所有观察者
 *
 * */
public class MainTest {
    public static void main(String[] args) {
        ConcreteObserver c1 = new ConcreteObserver("小明");
        ConcreteObserver c2 = new ConcreteObserver("小李");
        ConcreteObserver c3 = new ConcreteObserver("小张");

        ConcreteSubject bos = new ConcreteSubject("老板");
        bos.attach(c1);
        bos.attach(c2);
        bos.attach(c3);
        bos.attach(c1);

        ConcreteSubject s = new ConcreteSubject("家长");
        s.attach(c1);
        s.attach(c2);
        s.attach(c3);

        s.notify("啥时候回来啊");
        bos.notify("国庆都给我加班!");
    }
}

4,发布-订阅模式

相比观察者模式多了订阅中心。

1,发布者

package com.demo.base.design_partten.observer.publish_subscrice;

/**
 * 发布者
 * @author
 * */
public class Publisher {

    private String name;

    public String getName() {
        return name;
    }

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

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

2,订阅者

package com.demo.base.design_partten.observer.publish_subscrice;

/**
 * 订阅者
 * @author admin
 */
public class SubScriber {

    private String name;

    public String getName() {
        return name;
    }

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

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

    public void getMessage(Publisher publisher, String msg){
        System.out.println(String.format( "订阅者:%s 收到 %s 发布的消息:%s!", name, publisher.getName(), msg) );
    }
}

3,订阅中心接口

package com.demo.base.design_partten.observer.publish_subscrice;

/**
 * @author admin
 * 订阅中心
 */
public interface PublisherCentral {

    //添加发布者-订阅者
    public void addPublisher(Publisher publisher, SubScriber... subScriber);

    //删除发布者-订阅者
    public void removePublisher(Publisher publisher, SubScriber ... subScribers);

    //发布消息
    public void publishMsg(Publisher publisher, String msg);
}

4,订阅中心实现

package com.demo.base.design_partten.observer.publish_subscrice;

import java.util.*;

/**
 * @author admin
 */
public class PublisherCentralImpl implements PublisherCentral {

    private Map<Publisher, Set<SubScriber>> set = new HashMap<>();
    private String name;

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

    @Override
    public void addPublisher(Publisher publisher, SubScriber... subScriber) {
        Set<SubScriber> s = set.get(publisher);
        if(s == null){
            s = new LinkedHashSet<>(Arrays.asList(subScriber));
            set.put(publisher, s);
        }else{
            s.addAll(Arrays.asList(subScriber));
        }
    }

    @Override
    public void removePublisher(Publisher publisher, SubScriber... subScribers) {
        Set<SubScriber> s = set.get(publisher);
        if(s == null){
            return;
        }else{
            s.removeAll(Arrays.asList(subScribers));
        }
    }

    @Override
    public void publishMsg(Publisher publisher, String msg) {
        Set<SubScriber> s = set.get(publisher);
        System.out.println(String.format("%s 在 %s 发布了消息:%s", publisher.getName(), name, msg));
        if(s != null){
            for(SubScriber subScriber : s){
                subScriber.getMessage(publisher, msg);
            }
        }
    }
}

5,业务类

package com.demo.base.design_partten.observer.publish_subscrice;

/**
 * @author 发布-订阅模式
 */
public class MainTest {

    public static void main(String[] args) {

        //订阅中心
        PublisherCentral publisherCentral = new PublisherCentralImpl("报社一");

        Publisher p1 = new Publisher("大河网");
        Publisher p2 = new Publisher("河南网");
        Publisher p3 = new Publisher("东京网");
        publisherCentral.addPublisher(p1, new SubScriber("张三"), new SubScriber("李四"), new SubScriber("王五"));
        publisherCentral.addPublisher(p2, new SubScriber("张三"), new SubScriber("小六"));
        publisherCentral.addPublisher(p3, new SubScriber("join"));
        publisherCentral.publishMsg(p1, "大河网最新报道!");
        publisherCentral.publishMsg(p2, "今年还行!");
        publisherCentral.publishMsg(p3, "bird language!");
    }
}