设计模式之观察者模式

观察者模式

观察者模式

观察者模式( Observer Pattern),又叫发布-订阅( Publish/ Subscribe)模式、模型-视图( Model/View)模式、源-监听噐( Source/ Listener)模式或从属者( Dependents)模式。定义种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被自动更新,属于行为型模式。

观察者模式的核心是将观察者与被观察者解耦,类似于消息/广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。

UML类图如下

image-20210429234749416

从UML类图中,我们可以看到,观察者模式主要包含三种角色:

抽象主题(Subject):指被观察的对象(Observable)。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法;

具体主题(ConcreteSubject):具体被观察者,当其内状态变化时,会通知已注册的观察者;

抽象观察者(Observer):定义了响应通知的更新方法;

具体观察者(ConcrereObserver):在得到状态更新时,会自动做出响应。

观察者模式的应用场景

在软件系统中,当系统一方行为依赖于另一方行为的变动时,可使用观察者模式松耦谷联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。

观察者模式适用于以下几种应用场景:

1、当一个抽象模型包含两个方面内容,其中一个方面依赖于另一个方面;

2、其他一个或多个对象的变化依赖于另一个对象的变化;

3、实现类似广播机制的功能,无需知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播;

4、多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

通用代码

抽象观察者(Observer)

1
2
3
4
public interface IObserver<E>
{
void update(E e);
}

具体观察者(ConcrereObserver)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ConcreteObserver<E> implements IObserver<E>
{
private String name;

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

@Override
public void update(E e)
{
System.out.println(name + "收到通知,更新状态:" + e.toString());
}
}

抽象主题(Subject)

1
2
3
4
5
6
public interface ISubject<E>
{
boolean attach(IObserver<E> iObserver);
boolean detach(IObserver<E> iObserver);
void notify(E e);
}

具体主题(ConcreteSubject)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcreteSubject<E> implements ISubject<E>
{
private List<IObserver<E>> observerList = new ArrayList<>();

@Override
public boolean attach(IObserver<E> iObserver)
{
return observerList.add(iObserver);
}

@Override
public boolean detach(IObserver<E> iObserver)
{
return observerList.remove(iObserver);
}

@Override
public void notify(E e)
{
observerList.forEach(o -> o.update(e));
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main
{
public static void main(String[] args)
{
ConcreteObserver<String> concreteObserver1 = new ConcreteObserver<>("观察者1");
ConcreteObserver<String> concreteObserver2 = new ConcreteObserver<>("观察者2");

ConcreteSubject<String> concreteSubject = new ConcreteSubject<>();
concreteSubject.attach(concreteObserver1);
concreteSubject.attach(concreteObserver2);
concreteSubject.notify("subject");

concreteSubject.detach(concreteObserver1);
concreteSubject.notify("new subject");
}
}

输出

1
2
3
观察者1收到通知,更新状态:subject
观察者2收到通知,更新状态:subject
观察者2收到通知,更新状态:new subject

观察者模式在源码中的应用

Spring中的 Context Loaderlistener实现了 Servletcontextlistener接口。

Servlet Contextlistener接口又继承了 Eventlistener,在JDK中 Eventlistener有非常广泛的应用。

可以看一下源代码,Contextloaderlistener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.springframework.web.context;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {


public ContextLoaderListener() {
}


public ContextLoaderListener(WebApplicationContext context) {
super(context);
}

/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}


/**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}

}

ServletContextListener 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
package javax.servlet;

import java.util.EventListener;


public interface ServletContextListener extends EventListener {

public default void contextInitialized(ServletContextEvent sce) {
}

public default void contextDestroyed(ServletContextEvent sce) {
}
}

EventListener 就是一个空接口

1
2
3
4
5
6
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}

观察者模式的优缺点

优点:

1、观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则;

2、分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上;

3、实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。

缺点:

1、如果观察者数量过多,则事件通知会耗时较长;

2、事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件;

3、如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。

打赏

请我喝杯咖啡吧~

支付宝
微信