目录
定义
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
- 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
- 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
- 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
意图
创建了对象间的一种一对多的依赖关系,当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。
使用场景
1.jdk的Observable.java
有增加、删除、通知观察者。
Observerable.java的关键代码
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
2.publish-event
一般是在不同的bean直接进行信息传递,比如我们beanA的事件处理完后,需要beanB进行处理一些业务逻辑的时候这种情况就一般可以使用publish-event解决。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
原理
ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口来提供的,通过ApplicationContext的publishEvent()方法发布到ApplicationListener;。
一个事件模型有三个组成部分:被监听对象source(也称为事件源),事件event和监听对象listener。事件发布者在发布事件的时候->通知事件的监听者。
首先,由监听对象注册监听回调函数(Callback),当事件源触发了事件后,监听对象会收到事件源的信息,然后决定如何对事件源进行处理。
实现
自定义事件
需要继承 extends ApplicationEvent
import org.springframework.context.ApplicationEvent;
public class ArticleUpdateEvent extends ApplicationEvent {
public ArticleUpdateEvent(Article article) {
super(article);
}
public String getArticleId() {
return this.getSource().toString();
}
public static ArticleUpdateEvent newInstance(Article article) {
return new ArticleUpdateEvent(article);
}
}
定义事件监听器
需要实现 implements ApplicationListener <自定义事件类型>
做出主要的操作
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.apache.commons.collections.CollectionUtils;
import org.jfantasy.framework.dao.jpa.PropertyFilterBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class AddArticleUpdateListener implements ApplicationListener <ArticleUpdateEvent> {
...
@Autowired
protected Environment environment;
@Override
public void onApplicationEvent(ArticleUpdateEvent event) {
String agentId = environment.getProperty("AGENTID");
final String SYSTEMTYPE = environment.getProperty("SYSTEMTYPE");
Article article = (Article) event.getSource();
...
MessageWechat.TextMessage textMessage = null;
Message.TextMessage message = null;
...
...
List<Employee> dbEmployee = employeeService.findAll(builder.build());
Set <String> dbIds = dbEmployee.stream().map(Employee::getId).map(String::valueOf).collect(Collectors.toSet());
if (article.getStatus() == ArticleStatus.unpublished) {
...
} else if (article.getStatus() == ArticleStatus.published) {
textMessage = MessageWechat.TextMessage.newBuilder()
.setContent("您在\"" + article.getChannels().get(0).getName() + "\"栏目的投稿《" + article.getTitle() + "》已通过审核。")
.build();
...
}
...
//给人员发送通知
if (SYSTEMTYPE != null && SYSTEMTYPE.equals("WECHAT")) {
sendMessage(users, null, textMessage, null);
} else {
sendMessage(users, agentId, null, message);
}
}
}
事件发送
使用 applicationContext.publishEvent(自定义事件类型);
@Autowired
private ApplicationContext applicationContext;
public Article update(Article article, boolean patch, Long id, List<PermissionInput> permissions) {
article.setId(id);
...
boolean messagepush = false;
if (oldArticle.getStatus() != ArticleStatus.published && article.getStatus() == ArticleStatus.published) {
if (article.getPublishedAt() == null){
article.setPublishedAt(DateUtil.now());
}
DataDictionary dataDictionary = dataDictionaryDao.findById(DataDictionaryKey.newInstance("yy:yy")).orElse(null);
if (dataDictionary != null && dataDictionary.getName().equals("jfy")) {
messageService.sendDingMessage(article.getId(),article.getTitle());
}
messagepush = true;
}
...
if (article.getStatus() != null && article.getStatus() != ArticleStatus.published) {
update.setPublishedAt(null);
this.articleDao.update(update, false);
}
if (messagepush) {
messagePush(update, permissions);
}
applicationContext.publishEvent(ArticleUpdateEvent.newInstance(article));
return result;
}
主要解决
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
优缺点
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。