当前位置:网站首页>【保姆级示例向】观察者模式
【保姆级示例向】观察者模式
2022-08-03 17:10:00 【AQin1012】
案例来自《重学Java设计模式》
案例场景
本案例是模拟每次小客车摇号通知的场景,如下图(截自《重学Java设计模式》)
文件结构
完整代码
EventListener.java
这个接口定义了监听事件要执行的方法,后面创建的监听器都会实现这个接口并重写该方法
public interface EventListener < T > {
/**
* observed do
* @param type
*/
void doEvent ( T type ) ;
}
MessageEventListener.java
消息监听器,用于监听摇号结果并给用户发送通知结果的消息(此处用日志模拟发送短信)
public class MessageEventListener implements EventListener < LotteryResult > {
public static Logger logger = LoggerFactory.getLogger( MessageEventListener.class ) ;
@Override
public void doEvent ( LotteryResult lotteryResult ) {
logger.info ( "给用户 {} 发送通知:{}",lotteryResult.getUid () ,lotteryResult.getMessage ()) ;
}
}
MQEventListener.java
消息队列监听器,用于监听摇号结果并记录结果(此处用日志模拟发送短信)
public class MQEventListener implements EventListener < LotteryResult > {
public static Logger logger = LoggerFactory.getLogger( MQEventListener.class ) ;
@Override
public void doEvent ( LotteryResult lotteryResult ) {
logger.info ( "记录用户 {} 摇号结果:{}",lotteryResult.getUid () ,lotteryResult.getMessage ()) ;
}
}
EventManager.java
这里定义了一个存储监听器的集合listeners
以及监听类型的枚举类EventType
,并封装了操作监听器的方法:
- 订阅方法:
subscribe()
- 取消订阅方法:
unsubscribe()
- 通知方法:
notify()
public class EventManager {
Map < Enum < EventType > , List < EventListener >> listeners = new HashMap <>() ;
public EventManager ( Enum < EventType > ... operations ) {
for ( Enum < EventType > operation : operations ) {
this.listeners.put ( operation, new ArrayList <>()) ;
}
}
/**
* 事件类型枚举类
*/
public enum EventType {
MQ, Message
}
/**
* 订阅
*
* @param eventType 事件类型
* @param listener 监听
*/
public void subscribe ( Enum < EventType > eventType, EventListener listener ) {
/**
* 将新订阅的监听器添加进存储不同监听器的map中
*/
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 lotteryResult 通知结果
*/
public void notify ( Enum < EventType > eventType, LotteryResult lotteryResult ) {
List < EventListener > users = listeners.get ( eventType ) ;
for ( EventListener user : users ) {
user.doEvent ( lotteryResult ) ;
}
}
}
LotteryResult.java
这个对象封装了返回所需要的信息
@Data
public class LotteryResult {
String uid;
String message;
Date time;
public LotteryResult ( String uid, String message, Date time ) {
this.uid = uid;
this.message = message;
this.time = time;
}
}
LotteryService.java
这里依赖了EventManager
这个操作监听器的方法类
- 构造函数中执行了监听器订阅操作
- 在调用
draw()
方法中执行了监听器的通知操作(针对此示例可以理解为监听器的监听对象执行事件draw()
时,会通知监听器(MessageEventListener
和MQEventListener
),此时监听器会遍历执行监听器中定义的doEvent()
方法)
- 定义了一个实际处理主线业务逻辑的
doDraw()
方法交由子类去实现
public abstract class LotteryService {
private EventManager eventManager;
public LotteryService () {
eventManager = new EventManager ( EventManager.EventType.MQ, EventManager.EventType.Message) ;
eventManager.subscribe ( EventManager.EventType.MQ, new MQEventListener ()) ;
eventManager.subscribe ( EventManager.EventType.Message, new MessageEventListener ()) ;
}
public LotteryResult draw ( String uid ) {
LotteryResult lotteryResult = doDraw ( uid ) ;
eventManager.notify ( EventManager.EventType.MQ, lotteryResult ) ;
eventManager.notify ( EventManager.EventType.Message, lotteryResult ) ;
return lotteryResult;
}
protected abstract LotteryResult doDraw ( String uid ) ;
}
LotteryServiceImpl.java
调用当前案例的主线业务逻辑:小客车摇号
public class LotteryServiceImpl extends LotteryService {
MinibusTargetService minibusTargetService = new MinibusTargetService () ;
@Override
protected LotteryResult doDraw ( String uid ) {
String lottery = minibusTargetService.lottery ( uid ) ;
return new LotteryResult ( uid, lottery, new Date ()) ;
}
}
MinibusTargetService.java
封装了小客车的相关操作
- 目前只有一个摇号服务,后续如有业务扩展,直接在此类中添加
- 如果有别的车型则新建该车型的类即可
public class MinibusTargetService {
/**
* mock lottery
*
* @param uid
* @return
*/
public String lottery ( String uid ) {
return Math.abs( uid.hashCode () % 2 ) == 0 ? "中签" : "未中签";
}
}
pom.xml
这个示例项目需要添加下依赖
<? xml version="1.0" encoding="UTF-8" ?>
< project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion > 4.0.0 </ modelVersion >
< groupId > org.example </ groupId >
< artifactId > Desigins </ artifactId >
< version > 1.0-SNAPSHOT </ version >
< properties >
< maven.compiler.source > 8 </ maven.compiler.source >
< maven.compiler.target > 8 </ maven.compiler.target >
</ properties >
< dependencies >
< dependency >
< groupId > org.projectlombok </ groupId >
< artifactId > lombok </ artifactId >
< version > 1.18.0 </ version >
</ dependency >
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
< dependency >
< groupId > org.slf4j </ groupId >
< artifactId > slf4j-api </ artifactId >
< version > 1.7.25 </ version >
</ dependency >
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
< dependency >
< groupId > ch.qos.logback </ groupId >
< artifactId > logback-classic </ artifactId >
< version > 1.2.3 </ version >
</ dependency >
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
< dependency >
< groupId > org.junit.jupiter </ groupId >
< artifactId > junit-jupiter-api </ artifactId >
< version > 5.2.0 </ version >
< scope > test </ scope >
</ dependency >
</ dependencies >
</ project >
测试
测试代码
@Slf4j
public class Test {
@org.junit.jupiter.api.Test
public void test_draw () {
LotteryService lotteryService = new LotteryServiceImpl () ;
LotteryResult result = lotteryService.draw ( "233444449484441" ) ;
System.out.println ( result ) ;
}
}
输出结果
边栏推荐
- CC2530_ZigBee+华为云IOT:设计一套属于自己的冷链采集系统
- 出海,是泡泡玛特的“解药”吗?
- Understand the recommendation system in one article: Outline 02: The link of the recommendation system, from recalling rough sorting, to fine sorting, to rearranging, and finally showing the recommend
- C专家编程 第2章 这不是Bug,而是语言特性 2.1 这关语言特性何事,在Fortran里这就是Bug呀
- 完整的搭建内网穿透ngrok详细教程(有图有真相)
- 兄弟组件通信context
- 【GAMES101】作业6 加速结构
- 面试突击71:GET 和 POST 有什么区别?
- 掌握Redis的Sentinel哨兵原理,可助你拿到25k的offer
- sibling component communication context
猜你喜欢
随机推荐
数据万象内容审核 — 共建安全互联网,专项开展“清朗”直播整治行动
CC2530_ZigBee+华为云IOT:设计一套属于自己的冷链采集系统
Excuse me this hologres dimension table is cached?How to Finished
After using Stream for many years, does collect still have these "saucy operations"?
为什么我用了Redis之后,系统的性能却没有提升
node连接mongoose数据库流程
vant自动上传图片/文件
5. Longest Palindromic Substring
怎么在opengauss中进行测试自己添加的新函数的性能(循环n次的运行时间)?
我想请问下,我们的数据库是在亚马逊,Dataworks 连不通,怎么办?
204. Count Primes
JS 字符串转 GBK 编码超精简实现
C专家编程 第1章 C:穿越时空的迷雾 1.6 它很棒,但它符合标准吗
新特性解读 | MySQL 8.0 在线调整 REDO
【AppCube】零代码小课堂开课啦
面试不再被吊打!这才是Redis分布式锁的七种方案的正确打开方式
11. Container With Most Water
IP属地如何高效率识别
九种方法!教你如何读取resources目录下的文件路径
通用型安全监测数据管理系统