前言:
如今朋友们对“java观察者模式的实现”可能比较注重,姐妹们都需要了解一些“java观察者模式的实现”的相关内容。那么小编也在网摘上网罗了一些对于“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观察者模式的实现