设计模式学习—观察者模式

1、什么是观察者模式?

​ 观察者模式是一种关注对象之间责任分配的行为模式。观察者模式定义了对象之间的一对多依赖关系,这样当一个对象更改状态时,它的所有依赖关系都会被通知并自动更新。此模式中的关键对象是subject和observer。理解观察者模式的另一种方法是发布者-订阅者关系的工作方式。

观察者模式有四个参与者:

  • Subject:用于注册观察员。对象使用此接口注册为观察者,并将自己从观察者中移除。

  • Observer :对象的更新接口,该接口应在主题发生更改时通知对象。所有观察者都需要实现观察者接口。此接口有一个方法update(),当主题的状态发生更改时,将调用该方法。

  • ConcreteSubject :存储对ConcreteObserver对象感兴趣的状态。当状态发生变化时,它向观察者发送一个通知。具体的主题总是实现主题接口。notifyobserver()方法用于在状态发生更改时更新所有当前的观察者。

  • ConcreateObserver :维护对ConcreteSubject对象的引用,并实现Observer接口。每个观察者注册一个具体的主题来接收更新。

    2、场景分析

    ​ 体育大厅是一个了不起的体育爱好者的体育网站。它们涵盖了几乎所有的体育项目,提供最新的新闻、信息、比赛日期、特定球员或球队的信息。现在,他们正计划以短信服务的形式提供现场解说或数十场比赛,但仅限于高级用户。他们的目标是在短时间间隔后发送短信实时比分、比赛情况和重要事件。作为用户,您需要订阅包,当有现场比赛时,您将收到一条短信到现场解说。该站点还提供了一个选项,可以随时从包中取消订阅。作为开发人员,体育游说团要求您为他们提供这个新功能。观察者的设计模式最适合这种情况,让我们看看这个模式,然后为体育大厅创建功能。

3、代码实现

public interface Subject {
    public void subscribeObserver(Observer observer);
    public void unSubscribeObserver(Observer observer);
    public void notifyObservers();
    public String subjectDetails();
}

主题界面的三个关键方法是:

  1. subscribeObserver,用于订阅观察员,或者我们可以说注册观察员,以便如果主题状态发生变化,应该通知所有这些观察员。

  2. unSubscribeObserver,用于取消订阅观察者,以便如果主题状态发生更改,不应通知此未订阅的观察者。

  3. notifyobserver,当主体状态发生更改时,此方法通知已注册的观察者。

    另外还有一个方法subjectDetails(),这是一个简单的方法,可以根据需要使用。在这里,它的工作是返回主题的细节。现在,让我们看看观察者接口

    public interface Observer {
        public void update(String desc);
        public void subscribe();
        public void unSubscribe();
    }
    
    1. update()方法由主体在观察者上调用,以便在主体状态发生更改时通知它。

    2. subscribe()方法用于用主题订阅自身。

    3. unSubscribe()方法来取消订阅主题本身。

      public interface Commentary {
          public void setDesc(String desc);
      }
      

      记者使用上面的界面来更新评论对象上的实时评论,该接口只包含一个用于更改具体subject对象状态的方法

      public class CommentaryObject implements Subject,Commentary {
          private final List<Observer> observers;
          private String desc;
          private final String subjectDetails;
          public CommentaryObject(List<Observer>observers,String subjectDetails){
              this.observers = observers;
              this.subjectDetails = subjectDetails;
          }
          @Override
          public void subscribeObserver(Observer observer) {
              observers.add(observer);
          }
          @Override
          public void unSubscribeObserver(Observer observer) {
              int index = observers.indexOf(observer);
              observers.remove(index);
          }
          @Override
          public void notifyObservers() {
              System.out.println();
              for(Observer observer : observers){
                  observer.update(desc);
              }
          }
          @Override
          public void setDesc(String desc) {
              this.desc = desc;
              notifyObservers();
          }
          @Override
          public String subjectDetails() {
              return subjectDetails;
          }
      }
      

      上面的类作为一个具体的主题工作,它实现了subject接口并提供了它的实现。它还存储对注册到它的观察者的引用。

      public class SMSUsers implements Observer {
          private final Subject subject;
          private String desc;
          private String userInfo;
      
          public SMSUsers(Subject subject, String userInfo) {
              if (subject == null) {
                  throw new IllegalArgumentException("No Publisher found.");
              }
              this.subject = subject;
              this.userInfo = userInfo;
          }
      
          @Override
          public void update(String desc) {
              this.desc = desc;
              display();
          }
      
          private void display() {
              System.out.println("[" + userInfo + "]: " + desc);
          }
      
          @Override
          public void subscribe() {
              System.out.println("Subscribing " + userInfo + " to " + subject.subjectDetails() + " ...");
              this.subject.subscribeObserver(this);
              System.out.println("Subscribed successfully.");
          }
      
          @Override
          public void unSubscribe() {
              System.out.println("Unsubscribing " + userInfo + " to " + subject.subjectDetails() + " ...");
              this.subject.unSubscribeObserver(this);
              System.out.println("Unsubscribed successfully.");
          }
      }
      

      上面的类是实现观察者接口的具体观察者类。它还存储对它订阅的主题的引用,并可选地存储用于显示用户信息的userInfo变量。现在,让我们测试这个例子。

      public class TestObserver {
          public static void main(String[] args) {
              Subject subject = new CommentaryObject(new ArrayList<>(), "Soccer Match[2014AUG24]");
              Observer observer = new SMSUsers(subject, "Adam Warner [New York]");
              observer.subscribe();
              System.out.println();
              Observer observer2 = new SMSUsers(subject, "Tim Ronney [London]");
              observer2.subscribe();
              Commentary cObject = ((Commentary) subject);
              cObject.setDesc("Welcome to live Soccer match");
              cObject.setDesc("Current score 0-0");
              System.out.println();
              observer2.unSubscribe();
              System.out.println();
              cObject.setDesc("It’s a goal!!");
              cObject.setDesc("Current score 1-0");
              System.out.println();
              Observer observer3 = new SMSUsers(subject, "Marrie [Paris]");
              observer3.subscribe();
              System.out.println();
              cObject.setDesc("It’s another goal!!");
              cObject.setDesc("Half-time score 2-0");
          }
      }
      

      正如您所看到的,最初有两个用户订阅了足球比赛并开始接收评论。但是后来有一个用户取消了订阅,所以用户没有再收到评论。然后,另一个用户订阅并开始获得评论。所有这一切都在不改变现有代码的情况下动态发生,不仅如此,假设公司想要在电子邮件上播放评论,或者任何其他公司想要与该公司合作来播放评论。您所需要做的就是创建两个新类,如UserEmail和ColCompany。并通过实现observer接口使它们成为主题的观察者。只要主体知道它是一个观察者,它就会提供更新。

      4、何时使用观察者模式?

      1. 当抽象有两个方面时,一个依赖于另一个。将这些方面封装在单独的对象中,可以独立地改变和重用它们。
      2. 当对一个对象的更改需要更改其他对象时,您不知道需要更改多少对象。
      3. 当一个对象应该能够通知其他对象而不需要假设这些对象是谁时。换句话说,您不希望这些对象紧密耦合。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • 何时使用观察者模式 1、触发联动:当修改目标状态时就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法...
    哥哥是欧巴Vitory阅读 364评论 0 0
  • 设计模式分类 总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原...
    lifeline丿毅阅读 1,221评论 0 2
  • 定义 定义对象之间一对多的依赖关系,使得每当一个对象发生变化时,则所有依赖于它的对象都会得到通知并自动更新。这个模...
    m1Ku阅读 204评论 0 1
  • 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则...
    哈哈撒拉嘿阅读 400评论 0 0
  • (一)手绘地图 桌面放着那本手绘地图 那是我花了好多个 喝咖啡的夜晚 描好的旅行线路 只是为了一个遥远的美好 与你...
    海明子阅读 433评论 0 4