【问题标题】:When should we use Observer and Observable?我们什么时候应该使用 Observer 和 Observable?
【发布时间】:2012-11-24 12:45:03
【问题描述】:

一位面试官问我:

什么是ObserverObservable,我们应该在什么时候使用它们?

我不知道这些术语,所以当我回到家开始在 Google 上搜索 ObserverObservable 时,我从不同的资源中找到了一些要点:

1) Observable 是一个类,Observer 是一个接口。

2) Observable 类维护Observers 的列表。

3) 当Observable 对象更新时,它会调用其每个Observers 的update() 方法来通知它已更改。

我找到了这个例子:

import java.util.Observable;
import java.util.Observer;

class MessageBoard extends Observable
{
    public void changeMessage(String message) 
    {
        setChanged();
        notifyObservers(message);
    }
}

class Student implements Observer 
{
    @Override
    public void update(Observable o, Object arg) 
    {
        System.out.println("Message board changed: " + arg);
    }
}

public class MessageBoardTest 
{
    public static void main(String[] args) 
    {
        MessageBoard board = new MessageBoard();
        Student bob = new Student();
        Student joe = new Student();
        board.addObserver(bob);
        board.addObserver(joe);
        board.changeMessage("More Homework!");
    }
}

但我不明白为什么我们需要ObserverObservablesetChanged()notifyObservers(message) 方法有什么用?

【问题讨论】:

标签: java design-patterns observable observer-pattern observers


【解决方案1】:

您有一个学生和留言板的具体示例。学生通过将自己添加到希望在新消息发布到留言板时得到通知的观察者列表中进行注册。当消息添加到消息板时,它会遍历其观察者列表并通知他们事件发生。

想想推特。当你说你想关注某人时,Twitter 会将你添加到他们的关注者列表中。当他们发送新推文时,您会在输入中看到它。在这种情况下,您的 Twitter 帐户就是 Observer,而您关注的人就是 Observable。

这个类比可能并不完美,因为 Twitter 更有可能是一个中介。但它说明了这一点。

【讨论】:

    【解决方案2】:

    用非常简单的术语来说(因为无论如何其他答案都在向您介绍所有官方设计模式,因此请查看它们以获取更多详细信息):

    如果您希望有一个由程序生态系统中的其他类监控的类,您说您希望该类是可观察的。 IE。它的状态可能会发生一些变化,您希望将其广播到程序的其余部分。

    现在,要做到这一点,我们必须调用某种方法。我们不希望 Observable 类与有兴趣观察它的类紧密耦合。只要满足某些标准,它就不会在乎自己是谁。 (想象一下它是一个广播电台,它不关心谁在收听,只要他们有一个调到他们频率的 FM 收音机)。为了实现这一点,我们使用了一个称为 Observer 的接口。

    因此,Observable 类将有一个 Observers 列表(即实现您可能拥有的 Observer 接口方法的实例)。每当它想广播某些东西时,它只是一个接一个地调用所有观察者的方法。

    结束这个谜题的最后一件事是 Observable 类如何知道谁感兴趣? 所以 Observable 类必须提供一些机制来允许观察者注册他们的兴趣。 addObserver(Observer o)等方法内部将Observer加入到观察者列表中,这样当有重要的事情发生时,它会循环遍历列表,调用列表中每个实例的Observer接口各自的通知方法。

    可能是他们在采访中没有明确询问您有关java.util.Observerjava.util.Observable 的问题,而是询问了通用概念。该概念是一种设计模式,Java 恰好提供了直接开箱即用的支持,以帮助您在需要时快速实现它。所以我建议你理解这个概念而不是实际的方法/类(你可以在需要时查找它们)。

    更新

    针对您的评论,实际的 java.util.Observable 课程提供以下设施:

    1. 维护java.util.Observer 实例的列表。有兴趣收到通知的新实例可以通过addObserver(Observer o) 添加,通过deleteObserver(Observer o) 删除。

    2. 维护内部状态,指定自上次通知观察者以来对象是否发生了变化。这很有用,因为它将您说Observable 已更改的部分与您通知更改的部分分开。 (例如,如果您有多个更改发生并且您只想在流程结束时通知而不是在每个小步骤时通知,它很有用)。这是通过setChanged() 完成的。因此,当您将某些内容更改为 Observable 并且您希望 Observers 的其余部分最终知道它时,您只需调用它。

    3. 通知所有观察者特定的Observable 已更改状态。这是通过notifyObservers() 完成的。这会在继续通知之前检查对象是否实际发生了变化(即调用了setChanged())。有 2 个版本,一个没有参数,一个有 Object 参数,以防您想在通知中传递一些额外信息。在内部发生的是,它只是遍历Observer 实例列表并为每个实例调用update(Observable o, Object arg) 方法。这告诉 Observer 是 Observable 对象发生了变化(您可能观察到多个),以及额外的 Object arg 可能携带一些额外的信息(通过 notifyObservers() 传递。

    【讨论】:

      【解决方案3】:

      定义

      当对象之间存在一对多关系时使用观察者模式,例如如果一个对象被修改,它的依赖对象将被自动通知,并且对所有依赖对象进行相应的更改。

      示例

      1. 假设你的永久地址发生了变化,那么你需要通知护照当局和泛卡当局。所以这里护照权威和泛卡权威是观察者,你是主体。

      2. 也在 Facebook 上,如果您订阅了某人,那么每当有新的更新发生时,您都会收到通知。

      何时使用:

      1. 当一个对象改变其状态时,所有其他从属对象必须自动改变其状态以保持一致性

      2. 当受试者不知道其拥有的观察者数量时。

      3. 当一个对象应该能够在不知道对象是谁的情况下通知其他对象时。

      第 1 步

      创建主题类。

      Subject.java

        import java.util.ArrayList;
        import java.util.List;
      
        public class Subject {
      
        private List<Observer> observers 
              = new ArrayList<Observer>();
        private int state;
      
        public int getState() {
          return state;
        }
      
       public void setState(int state) {
         this.state = state;
         notifyAllObservers();
       }
      
         public void attach(Observer observer){
           observers.add(observer);       
         }
      
        public void notifyAllObservers(){
          for (Observer observer : observers) {
           observer.update();
        }
      }   
      

      }

      第 2 步

      创建观察者类。

      Observer.java

      public abstract class Observer {
         protected Subject subject;
         public abstract void update();
      }
      

      第 3 步

      创建具体的观察者类

      BinaryObserver.java

      public class BinaryObserver extends Observer{
      
        public BinaryObserver(Subject subject){
           this.subject = subject;
           this.subject.attach(this);
        }
      
        @Override
        public void update() {
           System.out.println( "Binary String: " 
           + Integer.toBinaryString( subject.getState() ) ); 
        }
      

      }

      OctalObserver.java

      public class OctalObserver extends Observer{
      
         public OctalObserver(Subject subject){
           this.subject = subject;
          this.subject.attach(this);
       }
      
        @Override
        public void update() {
          System.out.println( "Octal String: " 
          + Integer.toOctalString( subject.getState() ) ); 
        }
      

      }

      HexaObserver.java

      public class HexaObserver extends Observer{
      
        public HexaObserver(Subject subject){
          this.subject = subject;
          this.subject.attach(this);
       }
      
        @Override
        public void update() {
           System.out.println( "Hex String: " 
          + Integer.toHexString( subject.getState() ).toUpperCase() ); 
      }
      

      }

      第 4 步

      使用 Subject 和具体的观察者对象。

      ObserverPatternDemo.java

       public class ObserverPatternDemo {
          public static void main(String[] args) {
             Subject subject = new Subject();
      
             new HexaObserver(subject);
             new OctalObserver(subject);
             new BinaryObserver(subject);
      
             System.out.println("First state change: 15");    
             subject.setState(15);
             System.out.println("Second state change: 10");   
             subject.setState(10);
       }
      

      }

      第 5 步

      验证输出。

      第一次状态变化:15

      十六进制字符串:F

      八进制字符串:17

      二进制字符串:1111

      第二次状态变化:10

      十六进制字符串:A

      八进制字符串:12

      二进制字符串:1010

      【讨论】:

      • 很好解释 :)
      • 我认为“定义”是一个错字。我希望这是一个错字。
      • 谈话很便宜。动手实例非常棒。
      【解决方案4】:

      它们是Observer design pattern 的一部分。 通常一个或多个obervers会获知一个observable的变化。这是“某事”发生的通知,您作为程序员可以定义“某事”的含义。

      使用此模式时,您可以将两个实体彼此分离 - 观察者变为可插入的。

      【讨论】:

      • 我会很感激,如果您在回答中添加board.changeMessage("More Homework!"); 的解释,我的意思是当changeMessage("More Homework!"); 被调用时会发生什么。
      【解决方案5】:

      Observer a.k.a 回调在 Observable 中注册。

      它用于通知,例如关于在某个时间点发生的事件。它广泛用于 Swing、Ajax、GWT 中用于调度操作,例如UI 事件(按钮单击、文本字段更改等)。

      在 Swing 中,您可以找到 addXXXListener(Listener l) 之类的方法,在 GWT 中,您有(异步)回调。

      由于观察者列表是动态的,观察者可以在运行时注册和注销。由于使用了接口,这也是一种将 observable 与观察者解耦的好方法。

      【讨论】:

        【解决方案6】:

        如果面试官要求在不使用 Observer 类和接口的情况下实现Observer design pattern,您可以使用以下简单示例!

        MyObserver 作为观察者接口

        interface MyObserver {
        
            void update(MyObservable o, Object arg);
        }
        

        MyObservable 作为 Observable 类

        class MyObservable
        {
            ArrayList<MyObserver> myObserverList = new ArrayList<MyObserver>();
        
            boolean changeFlag = false;
        
            public void notifyObservers(Object o)
            {
                if (hasChanged())
                {
                    for(MyObserver mo : myObserverList) {
                        mo.update(this, o);
                    }
                    clearChanged();
                }
            }
        
        
            public void addObserver(MyObserver o) {
                myObserverList.add(o);        
            }
        
            public void setChanged() {
                changeFlag = true;
            }
        
            public boolean hasChanged() {
                return changeFlag;
            }
        
            protected void clearChanged() {
                changeFlag = false;
            }
        
            // ...
        }
        

        您的 MyObserver 和 MyObservable 示例!

        class MessageBoard extends MyObservable {
          private String message;
        
          public String getMessage() {
            return message;
          }
        
          public void changeMessage(String message) {
            this.message = message;
            setChanged();
            notifyObservers(message);
          }
        
          public static void main(String[] args) {
            MessageBoard board = new MessageBoard();
            Student bob = new Student();
            Student joe = new Student();
            board.addObserver(bob);
            board.addObserver(joe);
            board.changeMessage("More Homework!");
          }
        }
        
        class Student implements MyObserver {
        
          @Override
          public void update(MyObservable o, Object arg) {
            System.out.println("Message board changed: " + arg);
          }
        
        }
        

        【讨论】:

          【解决方案7】:

          “我试图弄清楚,为什么我们需要 Observer 和 Observable”

          正如之前的答案已经说明的那样,它们提供了订阅观察者以接收可观察对象的自动通知的方法。

          数据绑定中可能有用的一个示例应用程序,假设您有一些编辑某些数据的 UI,并且您希望 UI 在数据更新时做出反应,您可以您的数据可观察,并为您的 UI 组件订阅数据

          Knockout.js 是一个 MVVM javascript 框架,它有一个很棒的入门教程,要查看更多的 observables,我真的建议阅读该教程。 http://learn.knockoutjs.com/

          我还在 Visual Studio 2008 起始页中找到了这篇文章(观察者模式是模型视图控制器 (MVC) 开发的基础http://visualstudiomagazine.com/articles/2013/08/14/the-observer-pattern-in-net.aspx

          【讨论】:

            【解决方案8】:

            我在这里写了一个观察者模式的简短描述:http://www.devcodenote.com/2015/04/design-patterns-observer-pattern.html

            帖子中的一个sn-p:

            观察者模式:本质上是在对象之间建立一对多的关系,在相互依赖的对象之间进行松耦合的设计。

            教科书定义:观察者模式定义了对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖关系都会得到通知并自动更新。

            以提要通知服务为例。订阅模型是理解观察者模式的最佳方式。

            【讨论】:

              【解决方案9】:

              当对象之间存在一对多关系时使用观察者模式,例如如果一个对象被修改,它的依赖对象将被自动通知。

              【讨论】:

                【解决方案10】:

                从 Java9 开始,这两个接口都已弃用,这意味着您不应再使用它们。见Observer is deprecated in Java 9. What should we use instead of it?

                但是,您仍然可能会收到有关他们的面试问题...

                【讨论】:

                  猜你喜欢
                  • 2021-09-07
                  • 2015-04-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-04
                  • 2017-09-13
                  • 2021-12-29
                  相关资源
                  最近更新 更多