一位面试官问我:

什么是观察者和可观察对象,什么时候应该使用它们?

我不知道这些术语,所以当我回到家,开始在谷歌上搜索关于观察者和Observable的信息时,我从不同的资源中找到了一些观点:

1) Observable是一个类,Observer是一个接口。 2) Observable类维护了一个观察者列表。 3)当一个Observable对象被更新时,它会调用它的每个观察者的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!");
    }
}

但我不明白为什么我们需要观察者和可观察对象?setChanged()和notifyObservers(message)方法是干什么用的?


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

认为Twitter。当你说你想关注某人时,Twitter会把你添加到他们的关注者列表中。当他们发送一条新的推文时,你会在输入中看到它。在这种情况下,你的Twitter账户是观察者,你关注的人是可观察对象。

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


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

当使用此模式时,您可以将这两个实体彼此分离—观察者变得可插入。


观察者也就是回调被注册在Observable中。

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

在Swing中你可以找到addXXXListener(Listener l)这样的方法,在GWT中你有(Async)回调。

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


简单来说(因为其他答案都是参考了所有的官方设计模式,所以更多的细节可以参考它们):

如果你想让一个类被程序生态系统中的其他类监视,你说你想让这个类是可观察的。也就是说,它的状态可能会有一些变化,你想要广播给程序的其余部分。

现在,要做到这一点,我们必须调用某种方法。我们不希望Observable类与感兴趣观察它的类紧密耦合。它不关心它是谁,只要它满足一定的标准。(假设这是一个广播电台,它并不关心谁在收听,只要他们把调频收音机调到他们的频率)。为了实现这一点,我们使用一个称为观察者的接口。

因此,Observable类将有一个观察者列表(例如,实现你可能拥有的观察者接口方法的实例)。每当它想要广播一些东西时,它就会一个接一个地在所有观察者上调用这个方法。

最后要解决的问题是Observable类如何知道谁感兴趣? 因此,Observable类必须提供某种机制来允许observer注册他们的兴趣。addObserver(Observer o)等方法在内部将观察者添加到观察者列表中,这样当重要事件发生时,它就会循环遍历列表,并调用列表中每个实例的观察者接口的各自通知方法。

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

更新

为了响应你的注释,实际的java.util.Observable类提供了以下功能:

Maintaining a list of java.util.Observer instances. New instances interested in being notified can be added through addObserver(Observer o), and removed through deleteObserver(Observer o). Maintaining an internal state, specifying whether the object has changed since the last notification to the observers. This is useful because it separates the part where you say that the Observable has changed, from the part where you notify the changes. (E.g. Its useful if you have multiple changes happening and you only want to notify at the end of the process rather than at each small step). This is done through setChanged(). So you just call it when you changed something to the Observable and you want the rest of the Observers to eventually know about it. Notifying all observers that the specific Observable has changed state. This is done through notifyObservers(). This checks if the object has actually changed (i.e. a call to setChanged() was made) before proceeding with the notification. There are 2 versions, one with no arguments and one with an Object argument, in case you want to pass some extra information with the notification. Internally what happens is that it just iterates through the list of Observer instances and calls the update(Observable o, Object arg) method for each of them. This tells the Observer which was the Observable object that changed (you could be observing more than one), and the extra Object arg to potentially carry some extra information (passed through notifyObservers().


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

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

}

定义

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

例子

比方说,你的永久地址改变了,那么你需要通知护照当局和pan卡当局。所以这里护照当局和泛卡当局是观察者,而你是受试者。 在Facebook上也一样,如果你订阅了某个人,那么无论何时有新的更新发生,你都会收到通知。

何时使用:

当一个对象改变其状态时,所有其他依赖对象必须自动改变其状态以保持一致性 当对象不知道它有多少观察者时。 当一个对象应该能够通知其他对象而不知道对象是谁时。

步骤1

创建Subject类。

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类。

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


"我试图弄清楚,为什么我们需要观察者和可观察对象"

正如前面的回答所述,它们提供了订阅观察者来接收可观察对象的自动通知的方法。

数据绑定是一个很有用的例子,假设你有一些UI在编辑数据,你想让UI在数据更新时做出反应,你可以让你的数据可观察,并将你的UI组件订阅到数据

Knockout.js是一个MVVM javascript框架,它有一个很好的入门教程,要想看到更多的可观察物,我真的建议你去阅读教程。http://learn.knockoutjs.com/

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


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

文章摘录如下:

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

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

例如,考虑一个提要通知服务。订阅模型是理解观察者模式的最佳方法。


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


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

然而,你仍然可能会在面试中遇到关于他们的问题……