观察者模式

观察者模式定义

首先需要说明的是,java已经帮我们创建好了被观察者和观察者接口,为了搞清楚观察者模式流程,我们先自己实现,然后再用java提供的接口来实现。

什么是观察者模式,观察者模式又叫发布订阅模式,简单点就是被观察者状态变化时,通知观察者

我们先从最简单的例子开始

创建观察者接口

public abstract class Observer {
  protected abstract void update(String context);
}

创建被观察者接口

public abstract class Observable {

  public Vector<Observer> obs = new Vector();

  protected void addObserver(Observer obs) {
    this.obs.add(obs);
  }

  protected void deleteObserver(Observer obs) {
    this.obs.remove(obs);
  }

  protected void notifyObservers(String context) {
    for (Observer observer : obs) {
      observer.update(context);
    }
  }

}

创建观察者接口实现类

public class ConcreteObserver extends Observer {
  @Override
  protected void update(String context) {
    System.out.println("Observer receive msg: " + context);
  }
}

创建被观察者接口实现类

public class ConCreteObservable extends Observable {

  public void doSomething() {
    System.out.println("Observable: I am watching movie");
    super.notifyObservers("observable is watching movie");
  }
}

客户端

public class Client {
  public static void main(String[] args) {
    // 定义一个被观察者
    ConCreteObservable conCreteObservable = new ConCreteObservable();
    // 添加观察者
    conCreteObservable.addObserver(new ConcreteObserver());
    conCreteObservable.doSomething();
  }
}
输出结果
Observable: I am watching movie
Observer receive msg: observable is watching movie

Observable 和 Observer

明白原理之后,我们用java自带的接口Observable ,Observer 来实现一个场景:气象局发布天气预警,电视台收到消息发布出去

public interface IWeather {
  //气象部门发布下雨公告
  void rain();
}
public class Weather extends Observable implements IWeather {

  @Override
  public void rain() {
    System.out.println("气象台发布下雨预警");
    super.setChanged();
    super.notifyObservers("今夜到明天上午局部地区有暴雨,请及时防范");
  }
}
public class CCTV implements Observer {
  @Override
  public void update(Observable o, Object arg) {
    //在实际中一般的做法是:观察者中的update方法接受两个参数,一个是被观察者,一个 是DTO(Data Transfer Object,据传输对象)
    System.out.println("cctv发布气象预告:"+ arg);
  }
}
public class Client {
  public static void main(String[] args) {
    Weather weather = new Weather();
    weather.addObserver(new CCTV());
    weather.rain();
  }
}
输出结果:
气象台发布下雨预警
cctv发布气象预告:今夜到明天上午局部地区有暴雨,请及时防范

java中事件监听

java中还有另外一种方式可以实现观察者模式,java中定义了EventObjectEventListener接口,EventObject 相当于被观察者,EventListener 相当于观察者,下面的例子是实现对门状态变化的监听。

首先创建一个事件,继承EventObject

public class DoorEvent extends EventObject {

  private Integer doorStatus;//0关闭 1打开

  /**
   * Constructs a prototypical Event.
   *
   * @param source The object on which the Event initially occurred.
   * @throws IllegalArgumentException if source is null.
   */
  public DoorEvent(Object source) {
    super(source);
  }

  public DoorEvent(Object source, Integer doorStatus) {
    super(source);
    this.doorStatus = doorStatus;
  }

  public Integer getDoorStatus() {
    return doorStatus;
  }

  public void setDoorStatus(Integer doorStatus) {
    this.doorStatus = doorStatus;
  }
}

接着创建监听器,监听该事件

public interface DoorListener extends EventListener {
  void doorEvent(DoorEvent doorEvent);
}
public class OpenDoorListener implements DoorListener {
  @Override
  public void doorEvent(DoorEvent doorEvent) {
    Integer doorStatus = doorEvent.getDoorStatus();
    if (1 == doorStatus) {
      System.out.println("door is opened");
    }
  }
}
public class CloseDoorListener implements DoorListener {

  @Override
  public void doorEvent(DoorEvent doorEvent) {
    Integer doorStatus = doorEvent.getDoorStatus();
    if (0 == doorStatus) {
      System.out.println("door is closed");
    }
  }
}

客户端

public class Client {
  public static void main(String[] args) {
    //创建监听事件
    DoorEvent event = new DoorEvent("open", 1);
    //开始监听
    List<DoorListener> listenerList = new ArrayList<DoorListener>();
    listenerList.add(new OpenDoorListener());
    listenerList.add(new CloseDoorListener());

    for (DoorListener doorListener : listenerList) {
      doorListener.doorEvent(event)
    }
  }
}

spring中的事件监听

我们来看看如何在spring中使用事件监听

下面我们来实现一个智能场景:主人下班回家,打开门之后,自动打开灯光,打开空调,打开电视,其实就是捷径。

同样的,先创建一个事件

public class OpenDoorEvent extends ApplicationEvent {

  public OpenDoorEvent(Object source) {
    super(source);
  }
}

依然创建三个监听类

打开灯光

@Component
public class OpenLightListener implements ApplicationListener<OpenDoorEvent> {

  @Override
  public void onApplicationEvent(OpenDoorEvent openDoorEvent) {
    String source = (String) openDoorEvent.getSource();
    // System.out.println(source);
    System.out.println("打开灯光...");
  }
}

打开电视

@Component
public class OpenTVListener implements ApplicationListener<OpenDoorEvent> {

  @Override
  public void onApplicationEvent(OpenDoorEvent openDoorEvent) {
    String source = (String) openDoorEvent.getSource();
    // System.out.println(source);
    System.out.println("打开电视...");
  }
}

打开空调

@Component
public class OpenKTListener implements ApplicationListener<OpenDoorEvent> {

  @Override
  public void onApplicationEvent(OpenDoorEvent openDoorEvent) {
    String source = (String) openDoorEvent.getSource();
    // System.out.println(source);
    System.out.println("打开空调...");
  }
}

创建service方法,使用ApplicationEventPublisher来发布事件

@Service
public class OpenDoorPublisherService {

  @Autowired
  private ApplicationEventPublisher applicationEventPublisher;

  public void openDoor() {
    OpenDoorEvent event = new OpenDoorEvent("dto json ....");
    System.out.println("欢迎主人回家...");
    applicationEventPublisher.publishEvent(event);
  }
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class Client {
  @Autowired
  private OpenDoorPublisherService openDoorPublisherService;
 
  @Test
  public void test() {
    openDoorPublisherService.openDoor();
  }
}

欢迎主人回家...
打开空调...
打开灯光...
打开电视...

如果我们像对打开顺序做排序,该怎么做呢

排序实现

SmartApplicationListener接口可以实现排序功能,因为它继承了Ordered接口,监听类做如下修改

@Component
public class OpenLightListener implements SmartApplicationListener {

  @Override
  public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
    return aClass == OpenDoorEvent.class;
  }

  @Override
  public boolean supportsSourceType(Class<?> aClass) {
    // 例如 aClass == User.class;
    return true;
  }

  @Override
  public void onApplicationEvent(ApplicationEvent applicationEvent) {
    System.out.println("打开灯光...");
  }

  @Override
  public int getOrder() {
    //数字越小优先级越高
    return 0;
  }
}

必须supportsEventType和supportsSourceType都返回true,才会执行onApplicationEvent方法。getOrder方法,数字越小优先级越高。

注解实现

能用注解搞定的事,干嘛还要手写这么多代码呢,时间拿来摸鱼它不香吗。

我们把三个监听类放到一起

@Service
public class OpenDoorListener {

  @Order(2)
  @EventListener(classes = OpenDoorEvent.class)
  public void openLight() {
    System.out.println("step2:打开灯光...");
  }

  @Order(3)
  @EventListener(classes = OpenDoorEvent.class)
  public void openTV() {
    System.out.println("step3:打开电视...");
  }

  @Order(1)
  @EventListener(classes = OpenDoorEvent.class)
  public void openKT() {
    System.out.println("step1:打开空调...");
  }
}
输出结果
欢迎主人回家...
step1:打开空调...
step2:打开灯光...
step3:打开电视...
posted @ 2021-08-30 13:05  hejiancao  阅读(48)  评论(0)    收藏  举报