何时应该使用Observer和Observable?

215

有一个面试官问我:

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

我之前不知道这些术语,所以当我回到家并开始谷歌关于ObserverObservable的内容时,我从不同的资源中找到了一些要点:

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

2) Observable类维护了一个Observer列表。

3) 当一个Observable对象被更新时,它调用每个Observerupdate()方法来通知它已经发生变化。

我找到了这个例子:

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) 方法有什么作用?


链接无法使用。@Androider,你能提供更新的链接吗? - prateek
如果您正在使用Java 6或更高版本,则可以尝试此链接:https://dzone.com/articles/java-ee6-events-lightweight - Ramiz Uddin
1
我强烈建议阅读这本书,以便更好地了解设计模式。它看起来很傻,但它是一个优秀的学习工具。书籍链接:http://shop.oreilly.com/product/9780596007126.do - countofmontecristo
1
请注意:Observer/Observable在Java 9中已被弃用。 替代方案:https://dev59.com/-1YO5IYBdhLWcg3wF9sg - Eren
这个链接 https://sourcemaking.com/design_patterns/observer 有用吗? - carloswm85
另一个:https://refactoring.guru/design-patterns/observer - carloswm85
10个回答

276
你有一个具体的例子,其中涉及到学生和留言板。学生通过将自己添加到观察者列表中来注册,以便在新消息发布到留言板时通知他们。当留言板上发布一条消息时,它会遍历其观察者列表,并通知他们事件已发生。
类比推特。当你说想要关注某个人时,Twitter会将你加入他们的关注者列表。当他们发送新的推文时,你就可以看到它了。在这种情况下,你的Twitter帐户是观察者,而你正在关注的人是可观察对象。
这个比喻可能不是完美的,因为推特更有可能是中介者。但它说明了这一点。

62

简单来说(因为其他答案已经引用了所有官方的设计模式,请参考它们获取更多详细信息):

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

现在,我们必须调用某种方法才能实现这一点。我们不希望Observable类与对其进行观察的类紧密耦合。它不关心是谁,只要它符合某些标准即可。(想象一下它是一家广播电台,只要听众拥有调谐在其频率上的FM收音机,它就不关心他们是谁)。为了实现这一目标,我们使用一个接口,称为Observer。

因此,Observable类将具有Observer列表(即可能实现Observer接口方法的实例)。每当它想要广播一些内容时,它只是依次调用所有观察者的方法。

关闭拼图的最后一件事是Observable类如何知道谁感兴趣?因此,Observable类必须提供某种机制以允许Observer注册其兴趣。例如,像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())。有两个版本,一个没有参数,一个带有Object参数,以便您可以通过通知传递一些额外的信息。在内部,它只是迭代Observer实例列表,并为每个实例调用update(Observable o, Object arg)方法。这告诉观察者哪个是发生更改的Observable对象(您可能正在观察多个对象),以及额外的Object arg以携带一些额外的信息(通过notifyObservers()传递)。


45

定义

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

示例

  1. 假设你的永久地址发生了变化,那么您需要通知护照机构和 PAN 卡机构。因此,在这里,护照机构和 PAN 卡机构是观察者,而您是主题。

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

何时使用

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

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

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

步骤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.java

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

第三步

创建具体观察者类

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

第四步

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

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


1
很好地解释了 :) - roottraveller
3
我认为“Defination”是一个打字错误。我希望它是一个打字错误。 - JohnJohn
说起来容易,做起来难。实际操作的例子非常棒。 - carloswm85

11

它们是观察者设计模式的一部分。

通常,一个或多个观察者会收到有关一个可观察对象更改的信息。这是一个通知,告诉你作为程序员可以定义“某事”发生了什么。

使用此模式时,您将两个实体相互解耦-观察者变得可插入。


如果您能在回答中添加board.changeMessage("More Homework!");的解释,我会非常感激,我的意思是当调用changeMessage("More Homework!");时会发生什么。 - Ravi

10
如果面试官要求实现观察者设计模式,但不允许使用观察者类和接口,你可以使用以下简单示例!

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

}

10

Observer,也称为回调函数,是在Observable中注册的。

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

在Swing中,您可以找到像addXXXListener(Listener l)这样的方法,在GWT中,您有(Async)callbacks。

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


6

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

正如之前的回答所述,它们提供了一种订阅观察者接收可观察对象自动通知的手段。

一个例子应用场景是在数据绑定中,假设您有一些用户界面编辑某些数据,并且希望当数据更新时,UI会做出反应,那么您可以使您的数据可观察,并订阅UI组件到数据。

Knockout.js是一个MVVM JavaScript框架,具有非常好的入门教程,如果想看到更多Observable的实际应用,请通过该教程进行学习。http://learn.knockoutjs.com/

我还在Visual Studio 2008起始页上发现了这篇文章(观察者模式是Model View Controller (MVC)开发的基础)。 http://visualstudiomagazine.com/articles/2013/08/14/the-observer-pattern-in-net.aspx


4
我在这里写了一个关于观察者模式的简短描述:http://www.devcodenote.com/2015/04/design-patterns-observer-pattern.html 以下是文章中的一小段:
观察者模式:它在对象之间建立了一种一对多的关系,并且在相互依赖的对象之间具有松散耦合的设计。
教科书上的定义:观察者模式定义了一种对象之间的一对多依赖关系,因此当一个对象改变状态时,所有依赖它的对象都会被通知并自动更新。
例如,考虑一个提醒服务。订阅模型是最好理解观察者模式的方式。

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

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接