「保姆级示例向」观察者模式

案例来自《重学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

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

@Datapublic 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;    }} ![](https://upload-images.jianshu.io/upload_images/10995913-5c7600156501b728.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

LotteryService.java

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

  • 构造函数中执行了监听器订阅操作
  • 在调用draw()方法中执行了监听器的通知操作(针对此示例可以理解为监听器的监听对象执行事件draw()时,会通知监听器(MessageEventListenerMQEventListener),此时监听器会遍历执行监听器中定义的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

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

package com.aqin.listenerpattern;import java.util.Date;/***  @Description *  @Author  aqin1012 AQin.*  @Date  2022/8/2 1:31 PM*  @Version  1.0*/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

封装了小客车的相关操作

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

package com.aqin.listenerpattern;/***  @Description *  @Author  aqin1012 AQin.*  @Date  2022/8/2 3:55 PM*  @Version  1.0*/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  < groupId > org.example  < artifactId > Desigins  < version > 1.0-SNAPSHOT  < properties > < maven.compiler.source > 8  < maven.compiler.target > 8   < dependencies > < dependency > < groupId > org.projectlombok  < artifactId > lombok  < version > 1.18.0          < dependency > < groupId > org.slf4j  < artifactId > slf4j-api  < version > 1.7.25          < dependency > < groupId > ch.qos.logback  < artifactId > logback-classic  < version > 1.2.3          < dependency > < groupId > org.junit.jupiter  < artifactId > junit-jupiter-api  < version > 5.2.0  < scope > test    

测试

测试代码

package com.aqin.listenerpattern;import lombok.extern.slf4j.Slf4j;/***  @Description *  @Author  aqin1012 AQin.*  @Date  2022/8/2 4:09 PM*  @Version  1.0*/@Slf4jpublic class Test {@org.junit.jupiter.api.Test    public void test_draw ()  {LotteryService lotteryService = new LotteryServiceImpl () ;        LotteryResult result = lotteryService.draw ( "233444449484441" ) ;        System.out.println ( result ) ;    }} 

输出结果

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章