观察者模式

112 阅读5分钟

观察者模式 Observer

现实中的观察者模式:

一个拥有众多粉丝的明星: 每个粉丝都想了解自己喜欢的明星的所有最新动态。 因此,只要对明星感兴趣,粉丝就可以关注该明星,当有新的动态时,会通知所有关注的粉丝; 当失去兴趣时,就取消关注该明星。

在这里,可以将粉丝视为观察者,将明星视为主题

一、概念

GoF 定义: 定义对象之间的_一对多_依赖关系,以便一个对象改变状态时,所有依赖它的对象都会收到通知并自动更新。

在这种模式下,有多个观察者(对象)在观察某个特定的主题(对象)。 这种模型也被成为_发布-订阅模型_

  • 如果观察者对主题感兴趣,并且希望在主题内部发生变化时得到通知,则将自己注册到该主题;
  • 如果观察者对主题失去兴趣,只需要从主题上取消注册即可。

核心概念

  • 主题(Subject): 被观察的对象,当状态发生变化时,通知所有注册过的观察者;
  • 观察者(Observer): 观察者主题的对象;

观察者模式的优点:

  • 解耦合:将主题与观察者分离,降低耦合
  • 支持动态注册和取消注册:观察者可以随时动态注册或取消注册,灵活管理依赖关系
  • 广播通知:主体维护一个观察者列表,可以向所有观察者广播通知

UML 对模式的描述

代码实现:

interface IObserver {
    void update(int i);
}

class Observer1 implements IObserver {
    @Override
    public void update(int i) {
        System.out.println("Observer1: myValue in Subject is now: " + i);
    }
}

interface ISubject {
    void register(IObserver o);
    void unregister(IObserver o);
    void notifyObservers(int i);
}

class Subject implements ISubject {
    List<IObserver> observersList = new ArrayList<IObserver>();
    private int myValue;

    public int getMyValue() {
        return myValue;
    }

    public void setMyValue(int myValue) {
        this.myValue = myValue;
        // Notify observers
        notifyObservers(myValue);
    }

    @Override
    public void register(IObserver o) {
        observersList.add(o);
    }

    @Override
    public void unregister(IObserver o) {
        observersList.remove(o);
    }

    @Override
    public void notifyObservers(int updatedValue) {
        for (int i = 0; i < observersList.size(); i++) {
            observersList.get(i).update(updatedValue);
        }
    }
}

优化:将主题的通知方法设置为保护范围,子类在自己关心的领域中调用父类的通知方法实现功能。

代码实现:

interface IObserver {
    void update(String s, int i);
}

class Observer1 implements IObserver {
    @Override
    public void update(String s, int i) {
        System.out.println("Observer1: myValue in " + s + " is now: " + i);
    }
}

interface ISubject {
    void register(IObserver o);
    void unregister(IObserver o);
}

abstract class AbstractSubject implements ISubject {
    List<IObserver> observersList = new ArrayList<>();

    @Override
    public void register(IObserver o) {
        observersList.add(o);
    }

    @Override
    public void unregister(IObserver o) {
        observersList.remove(o);
    }

    protected void notifyObservers(int updatedValue) {
        for (int i = 0; i < observersList.size(); i++) {
            observersList.get(i).update(this.getClass().getSimpleName(), updatedValue);
        }
    }
}

class Subject1 extends AbstractSubject {
    private int myValue;

    public int getMyValue() {
        return myValue;
    }

    public void setMyValue(int myValue) {
        this.myValue = myValue;
        // Notify observers
        notifyObservers(myValue);
    }

}

二、 实现观察者

  1. 定义使用的域对象
public record OrderCreateEvent(String orderId) {
}
  1. 定义观察者接口:
public interface Observer<T> {
    void onEvent(T event);
}
  1. 定义 Email 通知:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class EmailNotificationService implements Observer<OrderCreateEvent> {
    @Override
    public void onEvent(OrderCreateEvent event) {
        // 发送邮件通知
        log.info("Sending email notification for order: {}", event.orderId());
    }
}
  1. 定义持久化功能:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class InventoryUpdateService implements Observer<OrderCreateEvent> {
    @Override
    public void onEvent(OrderCreateEvent event) {
        // 更新库存信息
        log.info("Updating inventory for order: {}", event.orderId());
    }
}
  1. 定义主题接口:
public interface Subject<T> {
    void register(Observer<T> o);
    void unregister(Observer<T> o);
}
  1. 定义主题抽象类:
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractSubject<T> implements Subject<T> {
    private final List<Observer<T>> observers = new ArrayList<>();

    @Override
    public void register(Observer<T> o) {
        observers.add(o);
    }

    @Override
    public void unregister(Observer<T> o) {
        observers.remove(o);
    }

    protected void notifyObservers(T event) {
        observers.forEach(observers -> observers.onEvent(event));
    }
}
  1. 订单主题服务类
import org.springframework.stereotype.Component;

@Component
public class OrderSubjectService extends AbstractSubject<OrderCreateEvent> {
    public void createOrder(String orderId) {
        OrderCreateEvent event = new OrderCreateEvent(orderId);
        // 通知所有观察者
        notifyObservers(event);
    }
}
  1. 测试:
@SpringBootTest
class OrderSubjectServiceTest {
    @Autowired
    private OrderSubjectService orderSubjectService;
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void createOrder() {
        EmailNotificationService emailNotificationService = applicationContext.getBean(EmailNotificationService.class);
        InventoryUpdateService inventoryUpdateService = applicationContext.getBean(InventoryUpdateService.class);

        orderSubjectService.register(emailNotificationService);
        orderSubjectService.register(inventoryUpdateService);

        orderSubjectService.createOrder("123");

        orderSubjectService.unregister(emailNotificationService);

        orderSubjectService.createOrder("456");
    }
}
  1. 预期输出:
Sending email notification for order: 123
Updating inventory for order: 123
Updating inventory for order: 456

三、 Spring 的支持

Spring Framework 提供了一套基于观察者模式的事件发布订阅机制,从而实现组件之间的协作:

Spring Framework 的事件发布订阅机制既可以支持普通非事务事件的发布订阅,也可以支持事务下的事件发布订阅。

  • ApplicationEvent: 事件对象父类,具体的事件类继承该类实现;

  • ApplicationEventPublisher: 事件发布接口,负责发布事件;

  • ApplicationListener / @EventListener: 普通非事务事件监听接口,监听器实现该接口,支持 SpEL 表达式实现基于条件的事件传递;

  • @TransactionEventListener: 事务监听接口修饰注解

    注意 @TransactionEventListener 必须与 @Transaction 配合使用。

    • phase: 使用 TransactionPhase 设置@TransactionEventListenerphase 属性,指定事件监听的事务的阶段。
    • fallbackExecution: 如果设置为 true 表示普通非事务事件也可以触发

可以使用 @Async 注解修饰事件接收方法,实现异步执行。

普通事件案例

  1. 定义事件:
import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class UserRegisteredEvent extends ApplicationEvent {
    private final User user;
    public UserRegisteredEvent(Object source, User user) {
        super(source);
        this.user = user;
    }
}
public record User(String email,
                   String phoneNumber,
                   boolean sendSms) {
}
  1. 发布事件:
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class UserRegisteredService {
    private final ApplicationEventPublisher eventPublisher;

    public void publishEvent(User user) {
        UserRegisteredEvent registeredEvent = new UserRegisteredEvent(this, user);
        eventPublisher.publishEvent(registeredEvent);
    }
}
  1. 监听事件
  • 实现 ApplicationListener 接口方式:
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SendEmailListener implements ApplicationListener<UserRegisteredEvent> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        User user = event.getUser();
        // 发送邮件逻辑
        log.info("发送邮件给: {}",user.email());
    }
}
  • 使用 @EventListener 注解:
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SendSmsListener {
    @EventListener(condition = "#event.user.sendSms() == true ")
    public void onApplicationEvent(UserRegisteredEvent event) {
        User user = event.getUser();
        // 发送短信逻辑
        log.info("发送短信给: {}", user.phoneNumber());
    }
}

事务事件案例

  1. 事务监听:
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;

import static org.springframework.transaction.event.TransactionPhase.*;

@Slf4j
@Component
public class OrderEventListener {
    @Async("taskExecutor") // 支持异步执行
    @TransactionalEventListener(phase = BEFORE_COMMIT)
    public void beforeCommit(OrderCreatedEvent event) {
        log.info("before commit order: {}", event.getOrder());
    }
    @TransactionalEventListener(phase = AFTER_COMMIT)
    public void afterCommit(OrderCreatedEvent event) {
        log.info("after commit order: {}", event.getOrder());
    }
    @TransactionalEventListener(phase = AFTER_ROLLBACK)
    public void afterRollback(OrderCreatedEvent event) {
        log.info("after rollback order: {}", event.getOrder());
    }
    @TransactionalEventListener(phase = AFTER_COMPLETION)
    public void afterCompletion(OrderCreatedEvent event) {
        log.info("after completion order: {}", event.getOrder());
    }
}
  1. 发布事件:即事件源
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@RequiredArgsConstructor
@Component
public class OrderTransactionService {
    private final ApplicationEventPublisher eventPublisher;
    private final OrderRepository orderRepository;

    @Transactional(rollbackFor = RuntimeException.class)
    public String createOrder(String orderDetails) {
        Order order = Order.builder().details(orderDetails).build();
        try {
            // 模拟事务回退
            if (orderDetails.equals("back")) {
                throw new RuntimeException("Error");
            }
            order = orderRepository.save(order);
            // 发布订单创建事件
            eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
            return order.id();
        } catch (RuntimeException e) {
            eventPublisher.publishEvent(new OrderCreatedEvent(this,order));
            throw e;
        }
    }
}