Skip to content

观察者

这在项目中多处使用。例如:授权手机号成功领取可赠送的会员卡,购物签到送积分等等。

定义

顾名思义,观察者模式(Observer Pattern)定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。

也可以理解为事件

什么时候使用它

一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

优缺点

优点:

  • 观察者和被观察者是抽象耦合的。
  • 建立了一套触发机制。

缺点:

  • 一个被观察者对象有很多的直接和间接观察者的话,将所有的观察者都通知到会花费很多时间。

核心实现

  • 主题(Subject)

也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。

  • 观察者(Observer)

观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。

  • 具体主题(Concrete Subject)

具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。

  • 具体观察者(Concrete Observer)

具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。

示例

需求是,签到送积分。

先来定义一个主题(Subject):

java
/**
 * 主题
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
public class Subject {
   
   private List<Observer> observers = new ArrayList<Observer>();
 
   public void attach(Observer observer) {
      observers.add(observer);
   }

   public void signin() {
      // 执行签到逻辑
      ...
      notifyAllObservers();
   }
 
   public void notifyAllObservers() {
      for (Observer observer : observers) {
         observer.update();
      }
   }
}

再来定义观察者(Observer):

java
/**
 * 抽象观察者
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

再来定义具体观察者(Concrete Observer):

java
/**
 * 签到观察者
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
public class SigninObserver extends Observer {
 
   public SigninObserver(Subject subject) {
      this.subject = subject;
      this.subject.attach(this);
   }
 
   @Override
   public void update() {
      System.out.println("用户xxx签到,送x积分");
   }
}

接下来就是客户端调用了:

java
public static void main(String[] args) {
    Subject subject = new Subject();
    SigninObserver signinObserver = new SigninObserver(subject);

    subject.signin();
}

通过这个简单的示例,演示了如何定义一个观察者模式。

最佳实践

spring event 便是经典的观察者模式实现。我们可以很容易的去使用它。标品里大量在使用。

在分布式环境中,更理想的方案为 mq。单机环境下可以使用 spring event。

我们仍然可以通过接口来统一实现。例如标品提供的抽象实现如下:

java
/**
 * 消息发送服务
 *
 * @author mjyang
 */
public interface MqSendService {

	/**
	 * 事件发送
	 *
	 * @param event event
	 */
	void publish(Object event);
}
java
/**
 * spring event 事件发送处理实现类
 *
 * @author mjyang
 * @date 2021-08-27 17:07:32
 */
@Service
@Slf4j
@ConditionalOnProperty(prefix = "wmeimob.eventbus", name = "storage", havingValue = "local")
@RequiredArgsConstructor
public class EventBusLocalSendServiceImpl implements MqSendService {

	private final ApplicationEventPublisher applicationEventPublisher;	

	@Override
	public void publish(Object event) {
		if (log.isDebugEnabled()) {
			log.debug("消息发送,{}", event);
		}
		try {
		    this.applicationEventPublisher.publishEvent(event);
		} catch (Exception e) {
			log.error("消息发送失败,{}", event);
		}
	}
}
java
/**
 *  mq 事件发送处理实现类
 *
 * @author mjyang
 * @date 2021-08-27 17:07:32
 */
@Service
@Slf4j
@ConditionalOnProperty(prefix = "wmeimob.eventbus", name = "storage", havingValue = "mq")
@RequiredArgsConstructor
public class EventBusMQSendServiceImpl implements MqSendService {

	private final XXXMQ applicationEventPublisher;	

	@Override
	public void publish(Object event) {
		if (log.isDebugEnabled()) {
			log.debug("消息发送,{}", event);
		}
		try {
		    this.applicationEventPublisher.xxx(event);
		} catch (Exception e) {
			log.error("消息发送失败,{}", event);
		}
	}
}