JAVA【设计模式】观察者模式
一、定义
观察者模式:指多个对象存在一对多的依赖关系,当一个对象发生改变时,所有依赖它对象的通知并自动更新。这种模式有时又叫做发布-订阅模式,模型-视图模式
二、示例:
模拟场景:
1、模拟摇号抽车牌,摇号抽牌的结果会通过发送短信或者MQ消息给摇号人,需要对外部的⽤户做⼀些事件通知以及需要在主流程外再添加⼀些额外的辅助流程时该如何处理呢?
传统编码方式
把消息通知类的方法,耦合写入到摇号的方法内部,导致代码冗余,不满足单一职责原则
package com.qf.design.behavior.observe.tradition;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.baisc.LotteryService;
import com.qf.design.behavior.observe.baisc.MinibusTargetService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class LotteryServiceImpl implements LotteryService {
private Logger logger= LoggerFactory.getLogger(LotteryServiceImpl.class);
@Override
public LotteryResult doDraw(String uid) {
MinibusTargetService minibusTargetService=new MinibusTargetService();
String lottery = minibusTargetService.lottery(uid);
//发送短信通知
logger.info("发送给用户{}短信通知:{}",uid,lottery);
//发送mq消息
logger.info("发送给用户{}MQ消息通知:{}",uid,lottery);
return new LotteryResult(uid,lottery,new Date());
}
}
package com.qf.design.behavior.observe.tradition;
import com.alibaba.fastjson.JSON;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.baisc.LotteryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiTest {
private static Logger logger= LoggerFactory.getLogger(LotteryServiceImpl.class);
public static void main(String[] args) {
LotteryService lotteryService = new LotteryServiceImpl();
LotteryResult lotteryResult = lotteryService.doDraw("202212567");
logger.info("摇号结果:{}", JSON.toJSONString(lotteryResult));
}
}
观察者模式设计
定义接口EventListener 处理消息监听的逻辑
package com.qf.design.behavior.observe.design.event.listener;
import com.qf.design.behavior.observe.baisc.LotteryResult;
public interface EventListener {
void doEvent(LotteryResult lotteryResult);
}
短信消息
package com.qf.design.behavior.observe.design.event.listener;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MessageEventListener implements EventListener{
private Logger logger= LoggerFactory.getLogger(MessageEventListener.class);
/**
* 发送短信通知消息
* @param lotteryResult
*/
@Override
public void doEvent(LotteryResult lotteryResult) {
logger.info("发送给用户{}短信通知:{}",lotteryResult.getuId(),lotteryResult.getMsg());
}
}
MQ消息
package com.qf.design.behavior.observe.design.event.listener;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MQEventListener implements EventListener{
private Logger logger= LoggerFactory.getLogger(MQEventListener.class);
/**
* 发送mq消息
* @param lotteryResult
*/
@Override
public void doEvent(LotteryResult lotteryResult) {
logger.info("发送给用户{}MQ消息通知:{}",lotteryResult.getuId(),lotteryResult.getMsg());
}
}
消息管理:订阅,取消订阅,唤醒消息
package com.qf.design.behavior.observe.design.event;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.design.event.listener.EventListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EventManager {
Map<Enum<EventType>, List<EventListener>> enumListMap = new HashMap<>();
public EventManager(Enum<EventType>...operations) {
for (Enum<EventType> operation : operations) {
enumListMap.put(operation,new ArrayList<>());
}
}
public enum EventType{
MQ, Message
}
/**
* 订阅
* @param eventTypeEnum
* @param eventListener
*/
public void subscribe(Enum<EventType> eventTypeEnum,EventListener eventListener){
List<EventListener> eventListeners = enumListMap.get(eventTypeEnum);
eventListeners.add(eventListener);
}
/**
* 取消订阅
* @param eventTypeEnum
* @param eventListener
*/
public void unSubscribe(Enum<EventType> eventTypeEnum,EventListener eventListener){
List<EventListener> eventListeners = enumListMap.get(eventTypeEnum);
eventListeners.remove(eventListener);
}
/**
* 唤醒内容
* @param eventTypeEnum
* @param lotteryResult
*/
public void notify(Enum<EventType> eventTypeEnum,LotteryResult lotteryResult){
List<EventListener> eventListeners = enumListMap.get(eventTypeEnum);
for (EventListener eventListener : eventListeners) {
eventListener.doEvent(lotteryResult);
}
}
}
抽象类:重申抽奖的方法
package com.qf.design.behavior.observe.design.event;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.design.event.listener.EventListener;
import com.qf.design.behavior.observe.design.event.listener.MQEventListener;
import com.qf.design.behavior.observe.design.event.listener.MessageEventListener;
public abstract class LotteryService {
private EventManager eventManager;
public LotteryService(){
eventManager=new EventManager(EventManager.EventType.MQ,EventManager.EventType.Message);
EventListener mqEventListener = new MQEventListener();
EventListener messageEventListener = new MessageEventListener();
eventManager.subscribe(EventManager.EventType.MQ,mqEventListener);
eventManager.subscribe(EventManager.EventType.Message,messageEventListener);
}
public LotteryResult draw(String uid){
LotteryResult lotteryResult = doDraw(uid);
eventManager.notify(EventManager.EventType.MQ,lotteryResult);
eventManager.notify(EventManager.EventType.Message,lotteryResult);
return lotteryResult;
}
public abstract LotteryResult doDraw(String uid);
}
子类实现,只需要关注摇号方法
package com.qf.design.behavior.observe.design.event;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.baisc.MinibusTargetService;
import java.util.Date;
public class LotteryServiceImpl extends LotteryService {
@Override
public LotteryResult doDraw(String uid) {
MinibusTargetService minibusTargetService=new MinibusTargetService();
String lottery = minibusTargetService.lottery(uid);
return new LotteryResult(uid,lottery,new Date());
}
}
测试:ApiTest
package com.qf.design.behavior.observe.design;
import com.alibaba.fastjson.JSON;
import com.qf.design.behavior.observe.baisc.LotteryResult;
import com.qf.design.behavior.observe.design.event.LotteryService;
import com.qf.design.behavior.observe.design.event.LotteryServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiTest {
private static Logger logger= LoggerFactory.getLogger(ApiTest.class);
public static void main(String[] args) {
LotteryService lotteryService = new LotteryServiceImpl();
LotteryResult lotteryResult = lotteryService.draw("202212567");
logger.info("摇号结果:{}", JSON.toJSONString(lotteryResult));
}
}
UML关系图
总结:
从我们最基本的过程式开发以及后来使⽤观察者模式⾯向对象开发,可以看到设计模式改造后,拆分出了核⼼流程与辅助流程的代码。⼀般代码中的核⼼流程不会经常变化。但辅助流程会随着业务的各种变化⽽变化,包括; 营销 、 裂变 、 促活 等等,因此使⽤设计模式架设代码就显得⾮常有必要。
此种设计模式从结构上是满⾜开闭原则的,当你需要新增其他的监听事件或者修改监听逻辑,是不需要改动事件处理类的。但是可能你不能控制调⽤顺序以及需要做⼀些事件结果的返回继续操作,所以使⽤的过程时需要考虑场景的合理性。