• 观察者模式


          观察者模式允许多个观察者订阅一个主题,当主题的状态发生变化时,能够将这种变化通知到每个观察者。从主题的角度看,这是典型的一对多关系,即一个主题可以对应多个观察者。以订阅邮件为例,当订阅某一类主题内容(例如娱乐新闻,动漫等)时,如果该主题的内容有更新,那么每一个订阅该主题的人都会受到一封内容更新的邮件,这便是典型的观察者模式。

          要实现邮件订阅的功能,需要先定义一个主题接口,主题接口需要能够随时添加或者删除订阅人(观察者),并且在有内容更新时通知观察者,其定义如下:

     1 public interface Subject {
     2   //添加观察者
     3   void registerObserver(Observer observer);
     4 
     5   //移除观察者
     6   void removeObserver(Observer observer);
     7 
     8   //有新内容时通知观察者
     9   void notifyObserver();
    10 }

          与主题接口对应的是观察者接口,接口很简单,在有内容的时候更新状态:

    1 public interface Observer{
    2     void update();    //更新状态
    3 }

          主题接口有许多实现,这里选择娱乐新闻主题作为例子:

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class EntertainmentSubject implements Subject{
     5 
     6   List<Observer> observerList;
     7 
     8   public EntertainmentSubject(){
     9     this.observerList = new ArrayList<>();
    10   }
    11 
    12   @Override
    13   public void registerObserver(Observer observer) {
    14     this.observerList.add(observer);
    15   }
    16 
    17   @Override
    18   public void removeObserver(Observer observer) {
    19     int i = observerList.indexOf(observer);
    20     if(i >= 0){
    21       observerList.remove(i);
    22     }
    23   }
    24 
    25   @Override
    26   public void notifyObserver() {
    27     for(Observer observer : observerList){
    28       observer.update();
    29     }
    30   }
    31 
    32   // 假设内容变化时会调用这个接口
    33   public void contentUpdated(){
    34     notifyObserver();
    35   }
    36 }

          主题需要在内容发生变化的时候通知所有的观察者,这里使用contentUpdated()方法来实现,至于主题内容变化如何调用contentUpdated()方法则不在本文的讨论范围内,这里假设只要主题有更新就会调用这个方法。

          接下来定义两个具体的观察者:

     1 public class ConcreteObserver1 implements Observer{
     2 
     3   Subject subject;
     4   public ConcreteObserver1(Subject subject) {
     5     this.subject = subject;
     6   }
     7 
     8   @Override
     9   public void update() {
    10     System.out.println("ConcreteObserver1, 给你发个邮件,因为这里有新内容了。。。");
    11   }
    12 }
     1 public class ConcreteObserver2 implements Observer{
     2 
     3   Subject subject;
     4   public ConcreteObserver2(Subject subject) {
     5     this.subject = subject;
     6   }
     7 
     8   @Override
     9   public void update() {
    10     System.out.println("ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。");
    11   }
    12 }

          好了,所有的类都准备就绪,是时候写个测试来看一下观察者模式的使用了:

     1 public class ObserverTest {
     2   public static void main(String[] args){
     3     EntertainmentSubject subject = new EntertainmentSubject();
     4     ConcreteObserver1 observer1 = new ConcreteObserver1(subject);
     5     ConcreteObserver2 observer2 = new ConcreteObserver2(subject);
     6     subject.registerObserver(observer1);
     7     subject.registerObserver(observer2);
     8     subject.contentUpdated();   //模拟内容更新
     9 
    10     subject.removeObserver(observer1);   //observer退订
    11     subject.contentUpdated();
    12   }
    13 }

          输出:

    ConcreteObserver1, 给你发个邮件,因为这里有新内容了。。。
    ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。
    ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。

          开始的时候有两个观察者,内容更新的时候两个观察者都收到了通知,后来observer1取消了订阅,更新的时候就不再通知这个观察者了。

          当然,这里的观察者模式的逻辑很简单,现实中用到的观察者模式比这要复杂许多,不过底层的结构大体相似。而且,Java也提供了观察者模式的相关实现Observable和Observer,使用的时候需要将Subject换成Observable,需要注意的是Observable是类而不是接口,所以如果你的类已经继承了其他类,就无法使用Java的这套API了。

    参考:

    <<Head First设计模式>>

  • 相关阅读:
    状压DP【p1879】[USACO06NOV]玉米田Corn Fields
    Tarjan缩点+Spfa最长路【p3627】[APIO2009] 抢掠计划
    Tarjan缩点【p1726】上白泽慧音
    分层图【p4822】[BJWC2012]冻结
    Tarjan缩点+LCA【p2783】有机化学之神偶尔会做作弊
    线段树【p1607】[USACO09FEB]庙会班车Fair Shuttle
    better-scroll踩坑合集
    在浏览器上安装 Vue Devtools工具
    无法执行vue初始化命令
    vue-cli创建第一个项目(用git bash解决上下键移动选择问题)
  • 原文地址:https://www.cnblogs.com/NaLanZiYi-LinEr/p/11565703.html
Copyright © 2020-2023  润新知