首页 > 其他分享 >观察者模式在事件驱动中的运用

观察者模式在事件驱动中的运用

时间:2023-09-23 12:48:50浏览次数:38  
标签:逻辑 创建 观察者 private order 事件驱动 订单 运用 public

title: 观察者模式在事件驱动中的运用
categories:
  - Java
tags:
  - 设计模式
abbrlink: 53cb85e5
date: 2023-03-02 15:06:28

在面对一些较为复杂的业务时,合理的使用事件驱动设计,能够帮助我们对主业务逻辑和分支业务逻辑进行解耦,更好的实现开闭原则。

什么是观察者模式?

观察者模式的实现是基于一种发布订阅的机制来实现的,通常来说会有两种角色,发布者和感兴趣的订阅者,比方说有个卖衣服的店有件衣服的款式很火爆,很多人都前来购买,但是没货了,于是店家就让顾客留下电话号码,说等来货了我再打电话通知你们,终于这款爆款衣服到货了,店家准备打电话通知顾客来购买,到这里你可能觉得很完美。但是随着店家的生意越来越好,经常出现缺货,留下电话等货的顾客越来越多,到最后店家几乎不能做其它事情,只能不停的打电话通知顾客,非常的忙!店家心想这可不行,这样生意没法做了啊,全部把时间浪费在了打电话通知顾客取货的事情上,于是店家想了一个很棒的想法,他创建了一个好友群,然后让想要购买某件爆款衣服的顾客加入这个群,等到衣服到货时,他只需要在对应的群发一条衣服到货了的信息就行了,不需要再挨个给每个顾客打电话了。在这个故事里,发布者就是店家,而订阅者就是顾客,顾客们对那件衣服感兴趣就加入对应的群,等待群里的到货通知再做一步行动。

使用事件对复杂业务解耦

初版代码

接下来以一个常被举例的业务逻辑 创建订单 这个业务场景为例子来说明,如何使用事件来对复杂业务逻辑进行解耦。

假设你有一个 OrderApplicationService 服务,并且对外提供 createOrder 方法来创建订单:

public class OrderApplicationService {

    private final StockService stockService;
    private final OrderRepository orderRepository;
    private final BuyerCenterService buyerCenterService;
    private final SellerCenterService sellerCenterService;
    
    @Transactional
    public void createOrder(Order order) {
        // 1.校验订单信息数据
        verify(order);
        // 2.扣减库存
        stockService.deduction(order);
        // 3.保存订单信息
        orderRepository.save(order);
        // 4.给用户发送订单创建成功站内信息通知
        buyerCenterService.sendNew(order);
        // 5.发送站内信息通知商家处理订单
        sellerCenterService.sendNews(order);
    }
}

在创建订单的时候,需要对订单数据合法性校验然后扣减商品库存保存订单信息,最后发送站内信息通知买家(用户)和卖家(商家)订单处理结果即可。最开始的业务需求较为简单这样写好像没什么问题,随着版本迭代业务需求变更,现在提出新的需求,需要在创建订单成功时,再发送手机短信通知买家,然后还要根据这笔订单给用户增加积分,那么你的创建订单方法很有可能就会变成下面这样:

public class OrderApplicationService {

    private final StockService stockService;
    private final OrderRepository orderRepository;
    private final BuyerCenterService buyerCenterService;
    private final SellerCenterService sellerCenterService;

    @Transactional
    public void createOrder(Order order) {
        // 1.校验订单信息数据
        verify(order);
        // 2.扣减库存
        stockService.deduction(order);
        // 3.保存订单信息
        orderRepository.save(order);
        // 4.给用户发送订单创建成功站内信息通知
        buyerCenterService.sendNew(order);
        // 5.发送站内信息通知商家处理订单
        sellerCenterService.sendNews(order);
        // 6.给用户发送订单创建成功手机短信通知
        buyerCenterService.sendSMS(order);
        // 7.给用户增加积分
        buyerCenterService.calculatePoints(order);
        // N.更多需求...
    }
}

好像这样做也没什么问题,功能也能实现,但是随着业务需求的变更,你需要频繁的去修改 createOrder 方法,违背了开闭原则的思想,最终会导致你的 createOrder 方法非常膨胀,失去了职责单一的初衷,它的业务语义越来越模糊,而且 createOrder 方法包含了太多分支逻辑比如发短信通知,增加积分,由于这些操作都在同一个事务,这将会造成任何一个分支逻辑的失败,都会导致整体事务的回滚从而导致创建订单失败,这显然不是我们愿意见到的,我们不希望用户增加积分失败这样的分支逻辑,却导致订单都创建失败。所以我们迫切需要将主逻辑与分支逻辑分离开来,接下来就轮到 事件 登场来对我们的主逻辑和分支业务逻辑进行解耦。

使用事件改造

public class OrderApplicationService {

    private final StockService stockService;
    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher publisher;

    @Transactional
    public void createOrder(Order order) {
        // 1.校验订单信息数据
        verify(order);
        // 2.扣减库存
        stockService.deduction(order);
        // 3.保存订单信息
        orderRepository.save(order);
        // 4.发布订单创建成功事件
        publisher.publishEvent(new OrderCreatedEvent(this,order));
    }
}

// 订单创建成功事件对象
public class OrderCreatedEvent extends ApplicationEvent {

    // 包含了本次事件的主体订单对象
    private final Order order;

    public OrderCreatedEvent(Object source,Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

通过改造createOrder这个方法,我们将主逻辑校验订单,扣减库存,持久化保存订单信息保留下来,最后借助 Spring 框架提供的事件机制,没有再额外自己再去实现一套订阅发布的观察者模式,在执行完创建订单最后一个主逻辑时,发布了一个 orderCreated 事件,那么刚才剩下的发送短信和给用户加积分的分支逻辑怎么去调用执行了?

public class BuyerCenterService {
    public void calculatePoints(Order order) {
        // TODO
    }

    @EventListener(OrderCreatedEvent.class)
    public void orderCreatedEvent(OrderCreatedEvent event) {
        // 拿到创建成功的订单对象
        Order order = event.getOrder();
        // 给用户计算增加积分
        calculatePoints(order);
    }
}

这里以增加用户积分这一个分支逻辑为例,其它的分支逻辑同理,都是监听订单创建成功事件做相应的逻辑处理即可,通过事件机制,我们就实现了主逻辑和分支业务逻辑的解耦,这样 createOrder 方法只需要专注于做好处理创建订单这一件事,避免一个方法功能业务知识过多。但是这样做还不能算真正的完全解耦,虽然我们把给用户增加积分这个分支逻辑剥离出去了,但是它还是属于 createOrder 这个方法的一部分,calculatePoints 方法的成功或失败还是会影响到创建订单的主逻辑事务,因为他们还是同步调用的,所以为了更进一步的实现解耦,需要使用异步事务。

异步事件

如果你是在 Spring 的环境下,那么通常实现异步很简单,你只需要在需要异步执行的方法添加一个 @Async 注解即可:

public class BuyerCenterService {

    public void calculatePoints(Order order) {
        // TODO
    }

    @Async // 添加注解实现异步
    @EventListener(OrderCreatedEvent.class)
    public void orderCreatedEvent(OrderCreatedEvent event) {
        // 拿到创建成功的订单对象
        Order order = event.getOrder();
        // 给用户计算增加积分
        calculatePoints(order);
    }
}

由于开启了异步,所以给用户增加积分的方法将不一定等待 createOrder 主逻辑正确执行完后再执行,也就意味着有可能创建订单其实失败了,但是你的计算增加积分却执成功了,这显然不是我们所期望的,我们期望的是只有当主逻辑正确执行成功了,分支逻辑才能被执行,这其中是有着先后顺序的依赖关系的。那么如何实现了?

TransactionalEventListener

public class BuyerCenterService {

    public void calculatePoints(Order order) {
        // TODO
    }

    @Async
    @TransactionalEventListener(value = OrderCreatedEvent.class, 
                                phase = TransactionPhase.AFTER_COMMIT)
    public void orderCreatedEvent(OrderCreatedEvent event) {
        // 拿到创建成功的订单对象
        Order order = event.getOrder();
        // 给用户计算增加积分
        calculatePoints(order);
    }
}

在 Spring 框架里面还额外提供了一个 @TransactionalEventListener 注解,它也有 @EventListener 同样的功能,但是在此基础还专门为解决事务之间的事件提供了一些额外的机制,其中该注解有一个 phase 参数,用来控制该监听方法何时执行:

public @interface TransactionalEventListener {
   // 控制监听器方法何时执行,默认值在事务提交成功后执行
   // 另外还有:
   // BEFORE_COMMIT 事务提交之前执行
   // AFTER_ROLLBACK 事务回滚之后执行
   // AFTER_COMPLETION 事务无论是回滚或提交都会执行  
   TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
}

一般来说我们使用默认的 AFTER_COMMIT 事务提交成功后执行,就能达到我们刚才的需求,就能保证创建订单主逻辑能够正确执行成功,其它的异步分支业务逻辑才会执行。

结语

通过分析创建订单的业务,我们从最开始的所有业务逻辑都在一个方法处理,到后来通过使用事务进行解耦拆分主逻辑和分支逻辑,到最后通过将分支逻辑异步化更进一步提升主逻辑的响应处理速度。

标签:逻辑,创建,观察者,private,order,事件驱动,订单,运用,public
From: https://www.cnblogs.com/shawnfux/p/17724210.html

相关文章

  • Vue.js 3.0 promise的运用
    ......
  • SQL语句的入门学习运用
    查看版本mysql-Vselectversion();查看帮助信息mysql--help 或者mysql-?登录mysql-hIP地址-p端口-u用户名-p密码说明:-hIP地址:连接到指定ip的数据库-p端口:需要连接数据库对应的端口(一般是3306)-u用户名:需要登录的用户名的密码-p密码:用户名登录数据库的密码列如:mysql-hlocaho......
  • CDN在互联网直播中的运用与性能优化
    本文分享自天翼云开发者社区《CDN在互联网直播中的运用与性能优化》,作者:不知不觉随着互联网技术的不断发展,互联网直播已成为人们获取信息、娱乐、学习等多种需求的重要途径之一。然而,在直播过程中,由于网络延迟、卡顿等问题,观众的观看体验往往受到影响。为了解决这些问题,内容分......
  • 设计模式-观察者模式
    设计模式提供了软件开发过程中的一些最佳实践,可以帮助我们解决常见的编程问题,提高软件的可维护性和可复用性,并使我们的代码更加健壮和灵活。设计模式可以带来以下好处:提高代码的可读性和可维护性、提高软件的可复用性、提高开发效率、提高系统的灵活性和可扩展性。今天我们讲一下观......
  • 观察者模式
    观察者模式,也称发布订阅模式,主题方发布,观察方订阅。observe.h/***Copyright(C)2023-09-1314:06zxinlog<[email protected]>**/#include<func.h>#include<iostream>#include<list>usingstd::cout;usingstd::endl;usingstd::list;classObserve......
  • Lnton羚通视频分析算法平台识别安全帽人脸联动闸机开关算法运用方案
    Lnton羚通的算法算力云平台具有突出的特点,包括高性能、高可靠性、高可扩展性和低成本。用户可以通过该云平台获得高效、强大的算法计算服务,快速、灵活地执行各种复杂的计算模型和算法,涉及机器学习、人工智能、大数据分析和图像识别等广泛领域。此外,云平台还提供丰富的算法库和工具,......
  • 6. 前端设计模式之观察者模式
    使用观察者模式,我们可以将某些对象(观察者)订阅到另一个对象(可观察对象)。当一个事件发生时,可观察对象会通知它的所有观察者!这个模式出境的概率就比较高了,无论是在前端还是后端,都能见到它的身影,特别跟事件有关的场景。从定义看这个模式涉及到两种对象,一种可观察对象也就是观察的......
  • 在.NET中实现事件驱动编程模式
    当在.NET中实现事件驱动编程模式时,你可以创建自定义事件和委托,然后让对象触发事件,以便其他对象可以订阅并响应这些事件。这种模式常用于构建可扩展和松散耦合的应用程序。以下是一个基本示例,演示如何在C#中实现事件驱动编程。步骤1:创建一个C#控制台应用程序首先,创建一个新的C#控......
  • 运用navicat for mysql实现定时备份
    使用navicatformysql实现定时备份首先打开需要进行备份的数据库,使之被高亮选中。点击“Schedule”菜单,点击后将出现如下图所示的功能界面:点击“NewBatchJob”,点击后将出现如下图所示的功能界面:“General”选项卡中显示了指定数据库连接下各个数据库实例中......
  • Lnton羚通机器视觉算法平台运用Yolov8检测矿山传送带下大块煤、料口堵塞算法分析
    Lnton羚通的算法算力云平台具有突出的特点,包括高性能、高可靠性、高可扩展性和低成本。用户可以通过该云平台获得高效、强大的算法计算服务,快速、灵活地执行各种复杂的计算模型和算法,涉及机器学习、人工智能、大数据分析和图像识别等广泛领域。此外,云平台还提供丰富的算法库和工具,......