目录
一、基本概念
二、UML类图
三、角色设计
四、代码实现
案例一
案例二
案例三
五、总结
一、基本概念
观察者先订阅被观察者对象,当被观察者的状态发生变化时,观察者可以及时收到消息,在这种模式当中,被观察者维护了一个观察者列表,并提供了添加、删除、通知观察者的方法。
二、UML类图
三、角色设计
角色 | 描述 |
---|---|
Blogger | 被观察者接口(博主),内部提供了注册、删除、通知观察者的方法 |
CSDNBlogger | 具体被观察者对象接口的实现类(CSDN),内部维护一个观察者列表 |
Fans | 观察者接口(粉丝),在被观察者通知时更新自己 |
FansObserver | 具体的观察者,当被通知时,可根据实际需求决定如何更新自己的状态 |
四、代码实现
这边我分享了三个案例,分别是通过自定义撰写、JDK源码封装和Spring框架封装三种去实现。
案例一
假设有一个被观察者:CSDN博主,它有2个粉丝分别是Jack和Tom,当CSDN发布了通知,对应的粉丝都会收到私信。
1、定义被观察者接口。
/**
* 博主
* @author HTT
*/
public interface Blogger {
/**
* 新增粉丝
*/
void addFans(Fans fans);
/**
* 移除粉丝
*/
void removeFans(Fans fans);
/**
* 通知粉丝
*/
void sendMessage(String message);
}
2、被观察者具体实现类。
CSDN博主:
import java.util.ArrayList;
import java.util.List;
public class CSDN implements Blogger {
private List<Fans> fansList = new ArrayList<>();
@Override
public void addFans(Fans fans) {
this.fansList.add(fans);
}
@Override
public void removeFans(Fans fans) {
this.fansList.remove(fans);
}
@Override
public void sendMessage(String message) {
for(Fans fans : fansList){
fans.receiveMessage(message);
}
}
}
3、定义观察者接口。
/**
* 粉丝
* @author HTT
*/
public interface Fans {
void receiveMessage(String message);
}
4、定义具体观察者实现类。
Jack粉丝:
public class JackFans implements Fans {
@Override
public void receiveMessage(String message) {
System.out.println("Jack收到了私信:"+message);
}
}
Tom粉丝:
public class TomFans implements Fans {
@Override
public void receiveMessage(String message) {
System.out.println("Tom收到了私信:"+message);
}
}
5、测试运行:
public class Main {
public static void main(String[] args) {
Blogger blogger = new CSDN();
Fans jackFans = new JackFans();
Fans tomFans = new TomFans();
blogger.addFans(jackFans);
blogger.addFans(tomFans);
blogger.sendMessage("CSDN发布了《关于社区整顿的通知》");
blogger.removeFans(jackFans);
blogger.sendMessage("CSDN发布了《关于博客发布调整的通知》");
}
}
6、运行结果:
案例二
这个案例依旧是实现上述逻辑,只不过我们使用JDK提供的接口去实现,过程如下。
1、定义被观察者具体实现类。
CSDN博主:
import java.util.Observable;
public class CSDN extends Observable {
@Override
public void notifyObservers(Object arg) {
//修改状态为可以群发
setChanged();
//调用父类的notifyObservers 群发消息
super.notifyObservers(arg);
}
}
2、定义观察者具体实现类。
Tom粉丝:
import java.util.Observable;
import java.util.Observer;
public class TomFans implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("Tom收到了私信:"+arg);
}
}
Jack粉丝:
import java.util.Observable;
import java.util.Observer;
public class JackFans implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("Jack收到了私信:"+arg);
}
}
3、测试运行:
import java.util.Observable;
import java.util.Observer;
public class Main {
public static void main(String[] args) {
Observable csdn = new CSDN();
Observer tomFans = new TomFans();
Observer jackFans = new JackFans();
csdn.addObserver(tomFans);
csdn.addObserver(jackFans);
csdn.notifyObservers("《关于整顿社区的通知》");
csdn.deleteObserver(tomFans);
csdn.notifyObservers("《关于更新CSDN社区的通知》");
}
}
4、运行结果:
有了JDK提供的接口去实现,整体代码结构更简洁和方便了!
案例三
这个案例依旧是上述逻辑,只不过我们这边使用Spring提供的封装事件的监听去实现,这边省去了搭建Spring框架的流程,直接看核心代码就行!
ApplicationEvent和Listener实际上就是Spring基于观察者模式设计的发布-订阅事件模型。
1、ApplicationEvent :自定义的事件对象,用于表示具体的事件。
CSDN博主:
import org.springframework.context.ApplicationEvent;
public class CSDN extends ApplicationEvent {
private String message;
public CSDN(Object source,String message) {
super(source);
this.message = message;
}
public String getMessage(){
return this.message;
}
public void setMessage(String message){
this.message = message;
}
}
2、ApplicationListener:事件监听器接口,用于监听特定事件。
Jack粉丝:
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class JackFans implements ApplicationListener<CSDN> {
@Override
public void onApplicationEvent(CSDN event) {
System.out.println("Jack收到私信:"+event.getMessage());
}
}
Tom粉丝:
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class TomFans implements ApplicationListener<CSDN> {
@Override
public void onApplicationEvent(CSDN event) {
System.out.println("Tom收到私信:"+event.getMessage());
}
}
3、单元测试
ApplicationContext:Spring上下文,用于广播ApplicationEvent,并通知相关的ApplicationListener。
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import javax.annotation.Resource;
@SpringBootTest
class ObserverApplicationTests {
@Resource
private ApplicationContext applicationContext;
@Test
void contextLoads() {
CSDN csdn = new CSDN(this,"发布了《关于整顿社区的通知》");
applicationContext.publishEvent(csdn);
}
}
4、运行结果
通过这种方式,不同的业务逻辑模块就可以不依赖于具体的实现,只通过监听特定事件来响应,从而实现解耦。
事件驱动的编程方式还有以下优点:
1、降低耦合度
2、提高模块间的独立性和可重用性
3、易于扩展和维护
总的来说,ApplicationEvent和Listener的设计初衷就是为了解耦和提高系统的扩展性、稳定性。
五、总结
这边我举了3个案例去更好的理解观察者模式,在我们日常开发的工作中要时刻提醒我们自己的编码思想,利用好这些优秀的前辈提供给我们的精髓,应用到实际业务场景中去,精益求精!