设计模式之观察者模式(行为模式)

92 阅读3分钟

观察者模式使用的场景:当目标对象发生变更时,一个或多个观察者可以接收变更的事件,比如在MQ中一对一或一对多的发布订阅,及一个生产者对应一个或多个消费者

优点:由于建立了一对多的统一触发机制可以实现目标与观察者的解藕。

缺点:当有多个观察者时可能会影响执行效率,因为目标对象内部会有存储观察者实例的容器,所谓的触发及触发容器内部的实例,因此当实例过多时会比较耗时。

类图:

观察者模式.gif

主要类及方法: 抽象目标:Subject

   #add 新增观察者实例方法到容器中
   #remove 移除观察者实例方法
   #notifyObserver 唤醒、触发 容器中的观察者 会调具体目标中的相同方法达到和观察者实例解耦的目的

具体目标:ConcreteSubject

   #notifyObserver 唤醒、触发 容器中的观察者   

观察者接口:Observer

     #response

观察者实例:ConcreteObserver1、ConcreteObserver2、ConcreteObserver3、、、、、

    #response

使用:

public class ObserverTest {
    public static void main(String[] args) {
        //创建具体目标类
        Subject subject = new ConcreteSubject();
        //创建观察者实例
        Observer obs1 = new ConcreteObserver1();
        Observer obs2 = new ConcreteObserver2();
        //添加观察者实例到抽象目标容器
        subject.add(obs1);
        subject.add(obs2);
        //调具体目标的notifyObserver 触发观察者执行
        subject.notifyObserver();
    }
}
//具体目标
public class ConcreteSubject extends Subject {
    @Override
    public void notifyObserver() {
        System.out.println("具体目标发生改变...");
        //触发所有观察者执行
        for (Object obs : observers) {
            ((Observer) obs).response();
        }
    }
}
//抽象目标
public abstract class Subject {
    //存储观察者实例
    protected List<Observer> observers = new ArrayList<Observer>();

    //增加观察者方法
    public void add(Observer observer) {
        observers.add(observer);
    }

    //删除观察者方法
    public void remove(Observer observer) {
        observers.remove(observer);
    }

    public abstract void notifyObserver(); //通知观察者方法
}
//观察者接口
public interface Observer {
    void response();//各实例处理逻辑
}
//观察者实现1
public class ConcreteObserver1 implements Observer {
    @Override
    public void response() {
        System.out.println("观察者1具体实现的业务逻辑、、、、、、");
    }
}
//观察者实现2
public class ConcreteObserver2 implements Observer {
    @Override
    public void response() {
        System.out.println("观察者2具体实现的业务逻辑、、、、、、");
    }
}

Java默认提供的观察者类及api:

目标类:Observable 其中包含 实例的新增、删除、触发 等方法

如下图所示:

1、Observable 的构造方法,在创建对象时会初始化 obs 容器

2、存储具体观察者实例的容器

3、新增观察者实例

4、删除观察者实例

5、触发观察者实例执行相应逻辑

6、changed 一个boolean类型的逻辑控制符

public void notifyObservers(Object arg) {
    /*
     * a temporary array buffer, used as a snapshot of the state of
     * current Observers.
     */
    Object[] arrLocal;

    synchronized (this) {
    //changed 使用的体现
        if (!changed)
            return;
        arrLocal = obs.toArray();
        clearChanged();
    }

    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}

image.png 观察者接口:Observer 具体的观察者实例实现此方法及可

package java.util;
public interface Observer {
  
    void update(Observable o, Object arg);
}

Guava提供的实现:

pom中引入对应的依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>
public class Guava {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        GuavaEvent1 guavaEvent1 = new GuavaEvent1();
        GuavaEvent2 guavaEvent2 = new GuavaEvent2();
        eventBus.register(guavaEvent1);
        eventBus.register(guavaEvent2);
        eventBus.post("消息体");
    }

    static class GuavaEvent1 {
        @Subscribe
        public void subscribe(String arg) {
            //接到通知后需要处理的具体业务逻辑
            System.out.println("GuavaEvent1收到通知" + arg);
        }
    }

    static class GuavaEvent2 {
        @Subscribe
        public void subscribe(String arg) {
            //接到通知后需要处理的具体业务逻辑
            System.out.println("GuavaEvent2收到通知" + arg);
        }
    }
}
控制台输出结果:
GuavaEvent1收到通知消息体
GuavaEvent2收到通知消息体

Process finished with exit code 0

Spring中观察者模式的使用? ApplicatcontextEvent

Redisson 中的发布订阅?