网站首页 > java教程 正文
介绍
观察者模式的使用场景非常广泛,小到代码层面的解耦,达到架构层面的系统解耦,再或者到一些产品的设计思路,都有这种模式的影子。
现在我们常说的基于事件驱动的架构,其实也是观察者模式的一种最佳实践。当我们观察某一个对象时,对象传递出的每一个行为都被看成是一个事件,观察者通过处理每一个事件来完成自身的操作处理。
生活中也有许多观察者模式的应用,比如,汽车和红绿灯的关系,‘红灯停,绿灯行’,在这个过程中交通信号灯是汽车的观察目标,而汽车是观察者。
观察者模式(observer pattern)的原始定义是:定义对象之间的一对多关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。
在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
观察者模式的别名有发布-订阅(Publish/Subscribe)模式,模型-视图(Model-View)模式、源-监听(Source-Listener)模式等。
结构图
抽象依赖于抽象,不让具体类之间产生依赖关系
案例代码
如果我们要实现一个买房摇号的程序,需要通过短信告知用户摇号结果,还需要向MQ中保存用户本次摇号的信息。
未使用观察者模式的代码如下
/**
* @Description: 开奖服务接口
* @author: zhuoyue
* @since: 2024/05/14 19:40
*/
public interface LotteryService {
/**
* 开奖之后的业务操作
* @param uId
* @return
*/
public LotteryResult lottery(String uId);
}
public class LotteryServiceImpl implements LotteryService {
//注入摇号服务
private DrawHouseService houseService = new DrawHouseService();
@Override
public LotteryResult lottery(String uId) {
//1.摇号
String result = houseService.lots(uId);
//发短信
System.out.println("发送短信通知用户ID为:"+uId+",您的摇号结果如下:"+result);
//发送MQ消息
System.out.println("记录用户摇号结果(MQ),用户ID:"+uId+",摇号结果:"+result);
return new LotteryResult(uId,result,new Date());
}
}
/**
* @Description: 开奖结果封装类
* @author: zhuoyue
* @since: 2024/05/14 19:38
*/
public class LotteryResult {
/**
* 用户Id
*/
private String uId;
/**
* 摇号信息
*/
private String msg;
/**
* 业务时间
*/
private Date dataTime;
public LotteryResult(String uId, String msg, Date dataTime) {
this.uId = uId;
this.msg = msg;
this.dataTime = dataTime;
}
@Override
public String toString() {
return "LotteryResult{" +
"uId='" + uId + '\'' +
", msg='" + msg + '\'' +
", dataTime=" + dataTime +
'}';
}
public String getuId() {
return uId;
}
public void setuId(String uId) {
this.uId = uId;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Date getDataTime() {
return dataTime;
}
public void setDataTime(Date dataTime) {
this.dataTime = dataTime;
}
}
public class DrawHouseService {
//摇号抽签
public String lots(String uId){
if(uId.hashCode() % 2 == 0){
return "恭喜ID为:"+uId+"的用户,在本次摇号中中签!";
}else{
return "很遗憾,ID为:"+uId+"的用户,您本次未中签";
}
}
}
使用观察者模式优化后的代码
上面的摇号业务中,发短信、发MQ消息是一个顺序调用的过程,但是除了摇号这个核心功能以外,发短信与记录信息到MQ的操作都不是主链路的功能,需要单独抽取出来,这样才能保证在后面的开发过程中保证代码的可扩展性和可维护性。
/**
* @Description: 事件监听接口
* @author: zhuoyue
* @since: 2024/05/14 20:04
*/
public interface EventListener {
void doEvent(LotteryResult result);
}
/**
* @Description: MQ发送事件监听类
* @author: zhuoyue
* @since: 2024/05/14 20:06
*/
public class MQEventListener implements EventListener {
@Override
public void doEvent(LotteryResult result) {
System.out.println("记录用户的摇号结果(MQ),用户ID为:"+result.getuId()
+",您的摇号结果为:"+result.getMsg());
}
}
/**
* @Description: 短信发送事件监听类
* @author: zhuoyue
* @since: 2024/05/14 20:06
*/
public class MessageEventListener implements EventListener {
@Override
public void doEvent(LotteryResult result) {
System.out.println("发送短信通知,用户ID:"+result.getuId()
+",您的摇号结果为:"+result.getMsg());
}
}
/**
* @Description: 事件处理类
* @author: zhuoyue
* @since: 2024/05/14 20:10
*/
public class EventManager {
public enum EventType{
MQ,Message
}
//监听器集合
Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();
public EventManager(Enum<EventType>... operations){
for (Enum<EventType> operation : operations) {
this.listeners.put(operation,new ArrayList<>());
}
}
/**
* 订阅
* @param eventType 事件类型
* @param listener 监听对象
*/
public void subscribe(Enum<EventType> eventType, EventListener listener){
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}
/**
* 取消订阅
* @param eventType 事件类型
* @param listener 监听对象
*/
public void unsubscribe(Enum<EventType> eventType,EventListener listener){
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}
/**
* 通知
* @param eventType 事件类型
* @param result 结果
*/
public void notify(Enum<EventType> eventType, LotteryResult result){
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.doEvent(result);
}
}
}
/**
* @Description: 开奖服务接口
* @author: zhuoyue
* @since: 2024/05/14 19:40
*/
public abstract class LotteryService {
private EventManager eventManager;
public LotteryService() {
//设置事件类型
eventManager = new EventManager(EventManager.EventType.Message, EventManager.EventType.MQ);
//订阅
eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener());
eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener());
}
public LotteryResult lotteryAndMsg(String uId){
LotteryResult result = lottery(uId);
//发送通知
eventManager.notify(EventManager.EventType.Message,result);
eventManager.notify(EventManager.EventType.MQ,result);
return result;
}
/**
* 开奖之后的业务操作
* @param uId
* @return
*/
public abstract LotteryResult lottery(String uId);
}
/**
* @Description: 开奖服务
* @author: zhuoyue
* @since: 2024/05/14 19:41
*/
public class LotteryServiceImpl extends LotteryService {
//注入摇号服务
private DrawHouseService houseService = new DrawHouseService();
@Override
public LotteryResult lottery(String uId) {
//1.摇号
String result = houseService.lots(uId);
return new LotteryResult(uId,result,new Date());
}
}
代码测试
@Test
public void test02(){
LotteryServiceImpl lotteryService = new LotteryServiceImpl();
LotteryResult result = lotteryService.lotteryAndMsg("12345678998754632146");
System.out.println(result);
}
总结
- 优点
- 降低了目标和观察者之间的耦合关系,两者之间是抽象耦合关系。
- 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】。
- 缺点
- 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
- 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃
- 使用场景
- 当一个对象的改变需要改变其他对象时。比如,商品库存数量发生变化时,需要通知商品详情页、购物车等系统改变数量
- 一个对象发生改变时只想要发送通知,而不需要知道接受者是谁。比如,订阅微信公众号的文章,发送者通过公众号发送,订阅者并不知道哪些用户订阅公众号。
- 上一篇: 如何用Java实现高效的多线程编程技巧
- 下一篇: Java图的实现(java编程图形)
猜你喜欢
- 2024-11-12 JAVA实现幂等性(java实现幂运算)
- 2024-11-12 序列化,Java的实现方式(java序列化概念)
- 2024-11-12 java实现羊了个羊(java实现直播功能)
- 2024-11-12 观察者模式的Java实现及应用(观察者模式的java实现及应用方法)
- 2024-11-12 适配器模式之Java实现(适配器模式代码实现)
- 2024-11-12 Java和Lua的完美结合:实现Java程序的动态扩展和脚本自动升级
- 2024-11-12 不了解这些底层实现及原理,别说你会 Java
- 2024-11-12 Java图的实现(java编程图形)
- 2024-11-12 如何用Java实现高效的多线程编程技巧
- 2024-11-12 java:手动实现一个IOC(实现 java)
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)