设计模式之观察者模式
2021/8/18 23:11:46
本文主要是介绍设计模式之观察者模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。如微博中一个明星发了一个消息,所有关注的用户都会看到这条消息。
结构
- Observer,观察者接口,定义目标通知时对应的更新方法。
- ConcreteObserver,具体观察者,接收目标通知,并进行相应的业务处理。
- Subject,目标对象,提供观察者注册和退订的方法,当目标状态发生变化时,通知所有已注册的观察者。
- ConcreteSubject,具体的目标对象,维护目标状态,通知所有已注册的观察者。
简单实现
观察者接口
/** * 观察者接口 */ public interface Observer { void update(Subject subject); }
具体观察者
/** * 观察者实现 */ public class ConcreteObserver implements Observer { @Override public void update(Subject subject) { ConcreteSubject concreteSubject = (ConcreteSubject) subject; System.out.println("ConcreteObserver," + concreteSubject.getSubjectState()); } }
另一个观察者
/** * 观察者实现 */ public class ConcreteObserver2 implements Observer { @Override public void update(Subject subject) { ConcreteSubject concreteSubject = (ConcreteSubject) subject; System.out.println("ConcreteObserver2," + concreteSubject.getSubjectState()); } }
目标对象
import java.util.ArrayList; import java.util.List; public class Subject { private List<Observer> observers = new ArrayList<>(); /** * 注册观察者 */ public void attach(Observer observer) { observers.add(observer); } /** * 删除观察者 */ public void detach(Observer observer) { observers.remove(observer); } /** * 通知所有观察者 */ protected void notifyObservers() { for (Observer observer : observers) { observer.update(this); } } }
具体的目标对象
public class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; notifyObservers(); } }
客户端
public class Client { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); subject.attach(new ConcreteObserver()); subject.attach(new ConcreteObserver2()); subject.setSubjectState("test"); } }
其实java中已经提供了 Observer 接口和 Observable 目标类,我们只需要在此基础上实现就可以了。
import java.util.Observable; import java.util.Observer; /** * 观察者实现 */ public class ConcreteObserver implements Observer { @Override public void update(Observable o, Object arg) { ConcreteSubject concreteSubject = (ConcreteSubject) o; System.out.println("ConcreteObserver," + concreteSubject.getSubjectState()); } }
import java.util.Observable; import java.util.Observer; /** * 观察者实现 */ public class ConcreteObserver2 implements Observer { @Override public void update(Observable o, Object arg) { ConcreteSubject concreteSubject = (ConcreteSubject) o; System.out.println("ConcreteObserver2," + concreteSubject.getSubjectState()); } }
具体的目标对象
import java.util.Observable; public class ConcreteSubject extends Observable { private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; setChanged(); notifyObservers(); } }
客户端
public class Client { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); subject.addObserver(new ConcreteObserver()); subject.addObserver(new ConcreteObserver2()); subject.setSubjectState("test"); } }
推模型和拉模型
观察者模式有两种实现方式,推模型和拉模型。
- 推模型
目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于在广播通信。 - 拉模型
目标对象在通知观察者的时候,只传递少量信息,如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据,
一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
根据上面的描述,我们上面的实现就是拉模型,下面我们实现一下推模型,基于我们自己定义的Observer接口。
/** * 观察者接口 */ public interface Observer { void update(String content); }
具体观察者
/** * 观察者实现 */ public class ConcreteObserver implements Observer { @Override public void update(String content) { System.out.println("ConcreteObserver," + content); } }
目标对象
public class Subject { private List<Observer> observers = new ArrayList<>(); /** * 注册观察者 */ public void attach(Observer observer) { observers.add(observer); } /** * 删除观察者 */ public void detach(Observer observer) { observers.remove(observer); } /** * 通知所有观察者 */ protected void notifyObservers(String content) { for (Observer observer : observers) { observer.update(content); } } }
具体目标对象
public class ConcreteSubject extends Subject { private String subjectState; public void setSubjectState(String subjectState) { this.subjectState = subjectState; notifyObservers(subjectState); } }
主要的变化在于update方法的参数由Subject(目标对象本身)变成了content(目标对象的详细信息)。
观察者模式在Guava和Spring的实现
Guava中的EventBus
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public class TestEventBus { public static void main(String[] args) { EventBus eventBus = new EventBus(); eventBus.register(new SimpleEventListener()); eventBus.post("this is a test event"); eventBus.post(12); } static class SimpleEventListener { @Subscribe public void test(String event) { System.out.println("received1 the event from eventbus: " + event); } @Subscribe public void test2(Integer event) { System.out.println("received2 the event from eventbus: " + event); } } }
输出结果为
received1 the event from eventbus: this is a test event received2 the event from eventbus: 12
使用Subscribe注解来注册观察者,通过参数类型来区分不同的目标对象,如String类型,Integer类型,使用post方法发布一条信息,找到这条消息的类型所对应的观察者列表,通知它们,通过反射执行方法。
Spring中的ApplicationEvent
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; public class TestApplicationEvent { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); context.publishEvent(new OrderSuccessEvent(new Object())); } public static class OrderSuccessEvent extends ApplicationEvent { public OrderSuccessEvent(Object source) { super(source); } } @Configuration public static class BeanConfig { @Bean public SmsService smsService() { return new SmsService(); } @Bean public SmsService2 smsService2() { return new SmsService2(); } } public static class SmsService implements ApplicationListener<OrderSuccessEvent> { @Override public void onApplicationEvent(OrderSuccessEvent event) { System.out.println("SmsService " + event.getSource()); } } public static class SmsService2 implements ApplicationListener<OrderSuccessEvent> { @Override public void onApplicationEvent(OrderSuccessEvent event) { System.out.println("SmsService2 " + event.getSource()); } } }
和guava中EventBus使用方法类似,通过ApplicationEvent的具体类型来区分不同的目标对象,publishEvent方法发布一个事件,从IOC容器中查找所有支持此event类型的观察者列表,通知它们。
总结
优点
- 目标对象和观察者之间抽象耦合,目标对象只知道观察者接口,不需要知道具体的观察者实现。
- 通过动态注册、删除观察者,实现动态联动。
- 支持广播通信。
缺点
- 如果目标对象和观察者之间存在循环依赖,可能导致系统崩溃。
- 如果忘记删除不必要的观察者,就会误发送消息,容易导致误操作。
本质
观察者模式的本质是触发联动。在程序运行期间动态的注册和删除观察者,可以变相的实现添加和删除某些功能处理。
使用场景
- 一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,这时就可以使用观察者模式,将这两者封装成观察者和目标对象。
- 在更改一个对象的时候,需要同时连带改变其他的对象,但不知道具体有哪些对象需要被连带改变。
- 当一个对象必须通知其他对象,但希望这个对象和被通知对象之间松散耦合。
参考
大战设计模式【2】—— 观察者模式
设计模式的征途—15.观察者(Observer)模式
设计模式(十八)——观察者模式(JDK Observable源码分析)
观察者模式(Observer模式)详解
Spring事件监听机制
研磨设计模式-书籍
这篇关于设计模式之观察者模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-15鸿蒙生态设备数量超8亿台
- 2024-05-13TiDB + ES:转转业财系统亿级数据存储优化实践
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?