目录
察者设计模式又被称作发布订阅模式,是定义对象之间一对多关系的一种设计模式。当一个对象A发生改变的时候,关注A的其他对象也会得到通知并发生改变。
观察者设计模式在项目中的使用是比较多的,例如一个经典的场景:用户注册成功之后,会发生赠送相应的积分(A),赠送优惠券(B),发送注册成功邮件(C)等动作。如果我们使用传统的调用,那么注册的这个接口,就要调用A,B,C三个接口。但是其实对于用户中心来说,是不关注积分赠送,优惠券赠送这类问题的。并且,如果以后产品又提了一个需求,用户注册成功之后,还得发送短信,生成一些其他的初始化信息,那我们是不是还得去改注册接口,这样的耦合性就太高了,也不容易扩展。
1.观察者模式的组成
观察者模式主要由四个部分组成:
- Subject(抽象主题):又被称为“被观察者”,一般是接口或者抽象类,提供三个抽象方法:增加观察者,删除观察者,通知观察者
- Observer(抽象观察者):又被称为“订阅者接口”,主要功能为提供一个更新的方法,当Subject发生变化的时候,能够更新自身的状态
- Concrete Subject(具体主题):实现抽象主题中的方法
- Concrete Observer(具体观察者):实现抽象观察者中的方法
类图可以表示如下(https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html):
2.简单实现观察者模式
下面以气象站的例子,写一个简单的观察者模式。当气象站发布了天气之后,需要进行统计和广播该天气变化。
package com.xiaohuihui.design.observer;
import java.util.List;
/**
* @Desription: 主题接口
* @Author: yangchenhui
* @Date: 2019/11/26 14:17
*/
public interface Subject {
/**
* 添加观察者
*
* @param observer
*/
void addObserver(Observer observer);
/**
* 删除观察者
*
* @param observer
*/
void removeObserver(Observer observer);
/**
* 通知观察者
*
* @param observerList
*/
void notifyObservers(List<Observer> observerList);
}
package com.xiaohuihui.design.observer;
/**
* @Desription: 订阅者接口
* @Author: yangchenhui
* @Date: 2019/11/26 14:19
*/
public interface Observer {
/**
* 更新状态
*
* @param temp
* @param pressure
*/
void update(float temp, float pressure);
}
package com.xiaohuihui.design.observer;
/**
* @Desription: 展示接口
* @Author: yangchenhui
* @Date: 2019/11/26 14:23
*/
public interface Display {
void display();
}
package com.xiaohuihui.design.observer;
import java.util.ArrayList;
import java.util.List;
/**
* @Desription: 气象站
* @Author: yangchenhui
* @Date: 2019/11/26 14:25
*/
public class WheatherData implements Subject {
/**
* 订阅者集合
*/
private List<Observer> observers;
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
public WheatherData() {
observers = new ArrayList<>();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if (observers != null && observers.contains(observer)) {
observers.remove(observer);
}
}
@Override
public void notifyObservers(List<Observer> observerList) {
if (observers != null && observers.size() > 0) {
for (Observer observer : observers) {
observer.update(temp, pressure);
}
}
}
/**
* 更新天气数据
*
* @param temp
* @param pressure
*/
public void updateWheatherData(float temp, float pressure) {
this.setTemp(temp);
this.setPressure(pressure);
this.notifyObservers(observers);
}
public List<Observer> getObservers() {
return observers;
}
public void setObservers(List<Observer> observers) {
this.observers = observers;
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package com.xiaohuihui.design.observer;
/**
* @Desription: 显示当前天气布告板
* @Author: yangchenhui
* @Date: 2019/11/26 14:44
*/
public class NowWeatherBroadcast implements Observer, Display {
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
/**
* 订阅主题
*/
private Subject subject;
/**
* 构造实例并注册到主题上
*
* @param subject
*/
public NowWeatherBroadcast(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
@Override
public void display() {
System.out.println("====> 现在的天气数据 temp: " + temp + " pressure: " + pressure);
}
@Override
public void update(float temp, float pressure) {
this.temp = temp;
this.pressure = pressure;
this.display();
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
package com.xiaohuihui.design.observer;
/**
* @Desription: 统计天气布告板
* @Author: yangchenhui
* @Date: 2019/11/26 14:53
*/
public class StatisticsWeatherBroadcast implements Observer, Display {
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
/**
* 订阅主题
*/
private Subject subject;
/**
* 构造实例并注册到主题上
*
* @param subject
*/
public StatisticsWeatherBroadcast(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
@Override
public void display() {
System.out.println("====> 开始统计天气数据 temp: " + temp + " pressure: " + pressure);
}
@Override
public void update(float temp, float pressure) {
this.temp = temp;
this.pressure = pressure;
this.display();
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
package com.xiaohuihui.design.observer;
/**
* @Desription:
* @Author: yangchenhui
* @Date: 2019/11/26 14:55
*/
public class ObserverTest {
public static void main(String[] args) {
WheatherData wheatherData = new WheatherData();
NowWeatherBroadcast broadcast1 = new NowWeatherBroadcast(wheatherData);
StatisticsWeatherBroadcast broadcast2 = new StatisticsWeatherBroadcast(wheatherData);
wheatherData.updateWheatherData(123.1F, 1.00F);
}
}
运行结果如下:
3.JDK实现观察者模式
除了自己实现观察者模式之外,JDK也给我们提供了相关的类。
可以通过继承Observable类成为具体的“被观察者”,实现Observer接口成为具体的“观察者”。
下面是代码示例:
package com.xiaohuihui.design.observer.java;
import java.util.Observable;
/**
* @author: yangch
* @date: 2019/11/26 22:36
* @Description: 使用java提供的观察者模式, 继承Observable相当于实现之前的Subject接口
*/
public class WheatherData2 extends Observable {
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
/**
* 更新天气数据
*
* @param temp
* @param pressure
*/
public void updateWheatherData(float temp, float pressure) {
this.setTemp(temp);
this.setPressure(pressure);
setChanged();
notifyObservers();
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package com.xiaohuihui.design.observer.java;
import com.xiaohuihui.design.observer.Display;
import java.util.Observable;
import java.util.Observer;
/**
* @author: yangch
* @date: 2019/11/26 22:40
* @Description:
*/
public class NowWeatherBroadcast2 implements Observer, Display {
/**
* 订阅主题
*/
private Observable observable;
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
public NowWeatherBroadcast2(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("====> 现在的天气数据 temp: " + temp + " pressure: " + pressure);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WheatherData2) {
WheatherData2 wheatherData = (WheatherData2) o;
this.setPressure(wheatherData.getPressure());
this.setTemp(wheatherData.getTemp());
display();
}
}
public Observable getObservable() {
return observable;
}
public void setObservable(Observable observable) {
this.observable = observable;
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package com.xiaohuihui.design.observer.java;
import com.xiaohuihui.design.observer.Display;
import java.util.Observable;
import java.util.Observer;
/**
* @author: yangch
* @date: 2019/11/26 22:40
* @Description: 实现Observer相当于将当前类标记为一个观察者类
*/
public class StatisticsWeatherBroadcast2 implements Observer, Display {
/**
* 订阅主题
*/
private Observable observable;
/**
* 温度
*/
private float temp;
/**
* 气压
*/
private float pressure;
public StatisticsWeatherBroadcast2(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("====> 统计天气数据 temp: " + temp + " pressure: " + pressure);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WheatherData2) {
WheatherData2 wheatherData = (WheatherData2) o;
this.setPressure(wheatherData.getPressure());
this.setTemp(wheatherData.getTemp());
display();
}
}
public Observable getObservable() {
return observable;
}
public void setObservable(Observable observable) {
this.observable = observable;
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package com.xiaohuihui.design.observer.java;
/**
* @author: yangch
* @date: 2019/11/26 22:48
* @Description:
*/
public class JavaObserverTest {
public static void main(String[] args) {
WheatherData2 wheatherData = new WheatherData2();
NowWeatherBroadcast2 nowWeatherBroadcast = new NowWeatherBroadcast2(wheatherData);
StatisticsWeatherBroadcast2 statisticsWeatherBroadcast = new StatisticsWeatherBroadcast2(wheatherData);
wheatherData.updateWheatherData(999.98F, 21.123F);
}
}
测试结果如下:
4.使用Spring实现观察者模式
- 通过继承ApplicationEvent成为“被观察者”
- 通过实现ApplicationListener成为“观察者”
- 通过实现ApplicationEventPublisherAware接口,获取到ApplicationEventPublisher对象,通过ApplicationEventPublisher发布事件
也可以直接使用@Resource或者@Autowired注解直接注入ApplicationEventPublisher对象。使用@EventListener来标识监听的方法。
下面是简单的代码示例:
package com.xiaohuihui.observer;
import org.springframework.context.ApplicationEvent;
/**
* @Desription: 用户注册事件
* @Author: yangchenhui
* @Date: 2020/6/4 19:21
*/
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(Object source) {
super(source);
}
}
package com.xiaohuihui.observer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
/**
* @Desription: 用户注册
* @Author: yangchenhui
* @Date: 2020/6/4 19:24
*/
@Service
public class UserServiceImpl implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
/**
* 用户注册
*
* @return
*/
public Boolean userRegister(String userName) {
System.out.println(userName + "用户注册成功");
applicationEventPublisher.publishEvent(new UserRegisterEvent(userName));
return true;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
package com.xiaohuihui.observer;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
/**
* @Desription: 发送注册成功邮件
* @Author: yangchenhui
* @Date: 2020/6/4 19:29
*/
@Service
public class EmailServiceImpl implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
System.out.println(userRegisterEvent.getSource() + "注册成功,发送注册邮件");
}
}
package com.xiaohuihui.observer;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
/**
* @Desription: 赠送积分
* @Author: yangchenhui
* @Date: 2020/6/4 19:44
*/
@Service
public class SocreServiceImpl {
@EventListener
public Boolean presentScore(UserRegisterEvent userRegisterEvent){
System.out.println(userRegisterEvent.getSource() + "注册成功,赠送1000积分");
return true;
}
}
package com.xiaohuihui.observer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* @Desription: 注解方式使用Spring事件发布订阅
* @Author: yangchenhui
* @Date: 2020/6/4 19:41
*/
@Service
public class UserServiceImpl2 {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 用户注册
*
* @return
*/
public Boolean userRegister(String userName) {
System.out.println(userName + "用户注册成功");
applicationEventPublisher.publishEvent(new UserRegisterEvent(userName));
return true;
}
}
package com.xiaohuihui.observer;
import com.xiaohuihui.XiaohuihuiSpringbootApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @Desription:
* @Author: yangchenhui
* @Date: 2020/6/4 19:52
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = XiaohuihuiSpringbootApplication.class)
public class UserServiceImplTest {
@Autowired
private UserServiceImpl userServiceImpl;
@Autowired
private UserServiceImpl2 userServiceImpl2;
@Test
public void userRegister() {
userServiceImpl.userRegister("xiaohuihui111");
userServiceImpl2.userRegister("xiaohuihui222");
}
}
运行结果如下: