208

一位面试官问我:

什么是Observer以及Observable何时应该使用它们?

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

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

2)Observable该类维护一个Observers 列表。

3)当一个Observable对象被更新时,它调用它update()的每个Observers的方法来通知它,它被改变了。

我找到了这个例子:

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!");
    }
}

但我不明白为什么我们需要Observerand ObservablesetChanged()notifyObservers(message)方法是干什么用的?

4

10 回答 10

274

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

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

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

于 2012-12-06T13:22:24.717 回答
62

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

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

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

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

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

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

更新

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

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

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

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

于 2012-12-06T13:39:17.293 回答
43

定义

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

例子

  1. 比方说,你的永久地址改变了,你需要通知护照当局和泛卡当局。所以这里护照局和泛卡局是观察者,你是主体。

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

何时使用它:

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

  2. 当对象不知道它拥有的观察者数量时。

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

第1步

创建主题类。

主题.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步

创建观察者类。

观察者.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步

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

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

于 2014-06-13T06:04:45.327 回答
11

它们是观察者设计模式的一部分。通常一个或多个obervers会获知一个observable的变化。这是“某事”发生的通知,您作为程序员可以定义“某事”的含义。

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

于 2012-12-06T13:25:46.673 回答
10

Observer aka 回调在 Observable 中注册。

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

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

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

于 2012-12-06T13:32:04.610 回答
10

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

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);
  }

}
于 2014-03-13T09:48:50.607 回答
6

“我试图弄清楚,为什么我们需要 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

于 2014-09-30T19:38:21.347 回答
4

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

帖子中的一个片段:

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

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

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

于 2015-04-17T03:44:49.763 回答
1

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

于 2016-11-08T06:15:04.593 回答
1

从 Java9 开始,这两个接口都已弃用,这意味着您不应再使用它们。请参阅Java 9 中不推荐使用 Observer。我们应该使用什么来代替它?

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

于 2017-12-06T05:31:46.800 回答