• 【保姆级示例向】观察者模式


    案例来自《重学Java设计模式》

    案例场景

    本案例是模拟每次小客车摇号通知的场景,如下图(截自《重学Java设计模式》)

    文件结构

    完整代码

    EventListener.java

    这个接口定义了监听事件要执行的方法,后面创建的监听器都会实现这个接口并重写该方法

    1. public interface EventListener < T > {
    2. /**
    3. * observed do
    4. * @param type
    5. */
    6. void doEvent ( T type ) ;
    7. }

    MessageEventListener.java

    消息监听器,用于监听摇号结果并给用户发送通知结果的消息(此处用日志模拟发送短信)

    1. public class MessageEventListener implements EventListener < LotteryResult > {
    2. public static Logger logger = LoggerFactory.getLogger( MessageEventListener.class ) ;
    3. @Override
    4. public void doEvent ( LotteryResult lotteryResult ) {
    5. logger.info ( "给用户 {} 发送通知:{}",lotteryResult.getUid () ,lotteryResult.getMessage ()) ;
    6. }
    7. }

    MQEventListener.java

    消息队列监听器,用于监听摇号结果并记录结果(此处用日志模拟发送短信)

    1. public class MQEventListener implements EventListener < LotteryResult > {
    2. public static Logger logger = LoggerFactory.getLogger( MQEventListener.class ) ;
    3. @Override
    4. public void doEvent ( LotteryResult lotteryResult ) {
    5. logger.info ( "记录用户 {} 摇号结果:{}",lotteryResult.getUid () ,lotteryResult.getMessage ()) ;
    6. }
    7. }

    EventManager.java

    这里定义了一个存储监听器的集合listeners以及监听类型的枚举类EventType,并封装了操作监听器的方法:

    • 订阅方法:subscribe()
    • 取消订阅方法:unsubscribe()
    • 通知方法:notify()

    1. public class EventManager {
    2. Map < Enum < EventType > , List < EventListener >> listeners = new HashMap <>() ;
    3. public EventManager ( Enum < EventType > ... operations ) {
    4. for ( Enum < EventType > operation : operations ) {
    5. this.listeners.put ( operation, new ArrayList <>()) ;
    6. }
    7. }
    8. /**
    9. * 事件类型枚举类
    10. */
    11. public enum EventType {
    12. MQ, Message
    13. }
    14. /**
    15. * 订阅
    16. *
    17. * @param eventType 事件类型
    18. * @param listener 监听
    19. */
    20. public void subscribe ( Enum < EventType > eventType, EventListener listener ) {
    21. /**
    22. * 将新订阅的监听器添加进存储不同监听器的map中
    23. */
    24. List < EventListener > users = listeners.get ( eventType ) ;
    25. users.add ( listener ) ;
    26. }
    27. /**
    28. * 取消订阅
    29. *
    30. * @param eventType 事件类型
    31. * @param listener 监听
    32. */
    33. public void unsubscribe ( Enum < EventType > eventType, EventListener listener ) {
    34. List < EventListener > users = listeners.get ( eventType ) ;
    35. users.remove ( listener ) ;
    36. }
    37. /**
    38. * 通知
    39. *
    40. * @param eventType 事件类型
    41. * @param lotteryResult 通知结果
    42. */
    43. public void notify ( Enum < EventType > eventType, LotteryResult lotteryResult ) {
    44. List < EventListener > users = listeners.get ( eventType ) ;
    45. for ( EventListener user : users ) {
    46. user.doEvent ( lotteryResult ) ;
    47. }
    48. }
    49. }

    LotteryResult.java

    这个对象封装了返回所需要的信息

    1. @Data
    2. public class LotteryResult {
    3. String uid;
    4. String message;
    5. Date time;
    6. public LotteryResult ( String uid, String message, Date time ) {
    7. this.uid = uid;
    8. this.message = message;
    9. this.time = time;
    10. }
    11. }

    LotteryService.java

    这里依赖了EventManager这个操作监听器的方法类

    • 构造函数中执行了监听器订阅操作
    • 在调用draw()方法中执行了监听器的通知操作(针对此示例可以理解为监听器的监听对象执行事件draw()时,会通知监听器(MessageEventListenerMQEventListener),此时监听器会遍历执行监听器中定义的doEvent()方法)
    • 定义了一个实际处理主线业务逻辑的doDraw()方法交由子类去实现

    1. public abstract class LotteryService {
    2. private EventManager eventManager;
    3. public LotteryService () {
    4. eventManager = new EventManager ( EventManager.EventType.MQ, EventManager.EventType.Message) ;
    5. eventManager.subscribe ( EventManager.EventType.MQ, new MQEventListener ()) ;
    6. eventManager.subscribe ( EventManager.EventType.Message, new MessageEventListener ()) ;
    7. }
    8. public LotteryResult draw ( String uid ) {
    9. LotteryResult lotteryResult = doDraw ( uid ) ;
    10. eventManager.notify ( EventManager.EventType.MQ, lotteryResult ) ;
    11. eventManager.notify ( EventManager.EventType.Message, lotteryResult ) ;
    12. return lotteryResult;
    13. }
    14. protected abstract LotteryResult doDraw ( String uid ) ;
    15. }

    LotteryServiceImpl.java

    调用当前案例的主线业务逻辑:小客车摇号

    1. public class LotteryServiceImpl extends LotteryService {
    2. MinibusTargetService minibusTargetService = new MinibusTargetService () ;
    3. @Override
    4. protected LotteryResult doDraw ( String uid ) {
    5. String lottery = minibusTargetService.lottery ( uid ) ;
    6. return new LotteryResult ( uid, lottery, new Date ()) ;
    7. }
    8. }

    MinibusTargetService.java

    封装了小客车的相关操作

    • 目前只有一个摇号服务,后续如有业务扩展,直接在此类中添加
    • 如果有别的车型则新建该车型的类即可

    1. public class MinibusTargetService {
    2. /**
    3. * mock lottery
    4. *
    5. * @param uid
    6. * @return
    7. */
    8. public String lottery ( String uid ) {
    9. return Math.abs( uid.hashCode () % 2 ) == 0 ? "中签" : "未中签";
    10. }
    11. }

    pom.xml

    这个示例项目需要添加下依赖

    1. < project xmlns="http://maven.apache.org/POM/4.0.0"
    2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
    4. < modelVersion > 4.0.0
    5. < groupId > org.example
    6. < artifactId > Desigins
    7. < version > 1.0-SNAPSHOT
    8. < properties >
    9. < maven.compiler.source > 8
    10. < maven.compiler.target > 8
    11. < dependencies >
    12. < dependency >
    13. < groupId > org.projectlombok
    14. < artifactId > lombok
    15. < version > 1.18.0
    16. < dependency >
    17. < groupId > org.slf4j
    18. < artifactId > slf4j-api
    19. < version > 1.7.25
    20. < dependency >
    21. < groupId > ch.qos.logback
    22. < artifactId > logback-classic
    23. < version > 1.2.3
    24. < dependency >
    25. < groupId > org.junit.jupiter
    26. < artifactId > junit-jupiter-api
    27. < version > 5.2.0
    28. < scope > test

    测试

    测试代码

    1. @Slf4j
    2. public class Test {
    3. @org.junit.jupiter.api.Test
    4. public void test_draw () {
    5. LotteryService lotteryService = new LotteryServiceImpl () ;
    6. LotteryResult result = lotteryService.draw ( "233444449484441" ) ;
    7. System.out.println ( result ) ;
    8. }
    9. }

    输出结果

  • 相关阅读:
    Flutter全面支持六大平台的开发,那鸿蒙呢?
    Mac的Safari浏览器如何打开检查元素【网页控制台】
    matlab knn方法快速实现,手把手教学
    互联网Java工程师面试题·Zookeeper 篇·第二弹
    PyQt5 JavaScript调用PyQt代码
    Jackson @JsonProperty重复字段处理
    希望大家给我一些关于实习和就业的建议
    python使用numpy的savetxt函数将数据内容写入指定文本文件中
    A2l文件解析
    Drupal view实现排序:未来升序,过去降序
  • 原文地址:https://blog.csdn.net/aqin1012/article/details/126127883