(09)观察者模式

概念

定义对象之间的一对多依赖关系(一个被观察者,多个观察者),当被观察者的状态发生改变时,所有的观察者都得到通知。也称为发布-订阅模式

  • 吃蛙来哒需要在大众点评APP叫号排队,有A/B/C三人都在大众点评APP叫号,等待APP通知什么时候去吃蛙来哒。那么APP与A/B/C三人就形成了一对多的依赖关系。可以理解成A/B/C三人订阅了大众点评APP,当APP通知什么时候用餐时,A/B/C三人都能得到相应的消息
  • 被观察者:大众点评APP。观察者:用户A/B/C

java实现方式

import java.util.ArrayList;
import java.util.List;

/**
 * 观察者
 */
public class Demo {
    public static void main(String[] args) {
        App app = new App();
        // app中添加两个观察者
        app.add(new BinaryObserver());
        app.add(new HexObserver());

        // 当状态发生变化,则通知相应的观察者
        app.setState(33);

        //运行结果
        //BinaryObserver 监听到状态发生变化:100001
        //HexObserver 监听到状态发生变化:21
    }
}

// 事件,当被观察者状态发生变化,则给观察者发送一个事件,一般事件中传递被观察者对象.
// 事件中还能记录发送时间等额外信息
class Event {
    private App source;

    public Event(App source) {
        this.source = source;
    }

    public App getSource() {
        return this.source;
    }
}

// 被观察者
class App {
    // 被观察者中,存储多个观察者
    private List<Observer> observerList = new ArrayList<>();
    private int state;

    public void add(Observer observer) {
        observerList.add(observer);
    }

    // 状态改变,通知所有观察者
    public void setState(int state) {
        this.state = state;
        observerList.forEach(t -> t.listener(new Event(this)));
    }

    public int getState() {
        return state;
    }
}

// 观察者接口。所有观察者类都要实现该接口
interface Observer {
    void listener(Event event);
}

// 具体观察者
class BinaryObserver implements Observer {
    public void listener(Event event) {
        System.out.println("BinaryObserver 监听到状态发生变化:" + Integer.toBinaryString(event.getSource().getState()));
    }
}

// 具体观察者
class HexObserver implements Observer {
    public void listener(Event event) {
        System.out.println("HexObserver 监听到状态发生变化:" + Integer.toHexString(event.getSource().getState()));
    }
}

BinaryObserver 监听到状态发生变化:100001
HexObserver 监听到状态发生变化:21

spring 观察者模式用法

普通代码写法

假设有个需求,创建订单成功后,需要保存订单日志,还要生成订单票据信息。

按以往代码逻辑,会是以下的写法。

@RestController
@RequestMapping("/order")
public class OrderController {
	
	/**
	 * 创建订单接口 
	 */
	@PostMapping("/createOrder")
	public String createOrder() {
		OrderEventMessage order = new OrderEventMessage();
		order.setOrderId("1001");
		order.setOrderName("苹果手机");

		// 保存订单
		orderService.createOrder(order);
		// 保存订单日志
		orderLogService.createOrderLog(order);
		// 创建订单票据信息
		orderInvoiceService.createOrderInvoice(order);
		return "创建订单成功!";
	}
}

上边写法,会存在以下问题

  • 保存订单日志、订单票据时,同步阻塞导致响应变慢,需要异步非阻塞的解决方案
  • 创建订单此时做的事情:创建订单、保存日志、创建单据,违反单一职责的原则。
  • 如果后续来了个 订单积分 的需求,此订单接口还需要改动。

为了解决上述的问题,可以使用 spring 的 观察者模式

spring的观察者模式写法

  1. 先定义订单事件,使用spring的发布订阅模式,需要继承 ApplicationEvent 对象
import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class OrderEvent extends ApplicationEvent {
    private OrderEventMessage message;

    public OrderEvent(Object source, OrderEventMessage message) {
        super(source);
        this.message = message;
    }
}
  1. 定义订单消息,存储具体的订单信息
import lombok.Data;

import java.io.Serializable;

@Data
public class OrderEventMessage implements Serializable {
    /** 订单号 */
    private String orderId;
    /** 订单名称 */
    private String orderName;
}
  1. 定义订单监听器。订单监听器相当于前边java例子中 具体观察者的 listener 方法。
    @EventListener: 使用注解的方式定义监听器
    @Async: 开启新线程异步执行监听器的内容
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class OrderEventListener {

	/**
	 * 订单日志监听器
	 */
	@EventListener
	@Async
	public void orderLogListener(OrderEvent event) {
		System.out.println("====== orderLog ====== " + event.getMessage().getOrderName());
	}

	/**
	 * 订单票据监听器
	 */
	@EventListener
	@Async
	public void orderInvoiceListener(OrderEvent event) {
		System.out.println("====== orderInvoice ====== " + event.getMessage().getOrderName());
	}
}
  1. 发布事件
@RestController
@RequestMapping("/order")
public class OrderController {

	@Autowired
	private ApplicationEventPublisher applicationEventPublisher;

	/**
	 * 创建订单接口
	 */
	@PostMapping("/createOrder")
	public String createOrder() {
		OrderEventMessage order = new OrderEventMessage();
		order.setOrderId("1001");
		order.setOrderName("苹果手机");

		// 保存订单
		orderService.createOrder(order);
		// 发布事件
		applicationEventPublisher.publishEvent(new OrderEvent(this, order));
		return "创建订单成功!";
	}
}

====== orderLog ====== 苹果手机
====== orderInvoice ====== 苹果手机

上边代码好处在于

  • 创建订单接口只需要做创建订单的功能
  • 订单创建好后,只需要把事件发布出去即可,后续有 订单积分 需求,此处也不需要修改代码,达到了解耦的目的
posted @ 2021-11-22 10:37  、嘎路的米。  阅读(48)  评论(0)    收藏  举报