我们什么时候应该使用Observer和Observable?


200

面试官问我:

什么是Observer,什么Observable时候应该使用它们?

我并不了解这些术语,因此当我回到家并开始使用Google Observer和Google进行搜索时Observable,发现了来自不同资源的一些观点:

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

但是我不明白为什么我们需要ObserverObservable?有什么setChanged()notifyObservers(message)方法?


链接无效。@Androider可以提供更新的链接吗?
prateek

如果您使用的是Java 6或更高版本,请尝试使用dzone.com/articles/java-ee6-events-lightweight
Ramiz Uddin

1
我强烈建议您阅读本书,以对设计模式有一个很好的了解。它虽然很愚蠢,但却是一种出色的学习工具。
countofmontecristo

1
大家请注意;Java 9中不推荐使用Observer / Observable。替代方法:stackoverflow.com/questions/46380073/…–
eren130

Answers:


265

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

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

这个类比可能并不完美,因为Twitter更有可能成为调解人。但这说明了这一点。


57

用非常简单的术语(因为无论如何,其他答案都将您引向所有正式的设计模式,因此请查看它们以了解更多详细信息):

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

现在,要做到这一点,我们必须调用某种方法。我们不希望Observable类与对它感兴趣的类紧密结合。只要满足特定条件,它就不在乎谁。(想象一下,这是一个广播电台,只要他们的频率调谐了FM收音机,它就不在乎谁在听)。为此,我们使用称为观察者的接口。

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

解决难题的最后一件事是,可观察的班级将如何知道谁感兴趣?因此,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()在继续进行通知之前检查对象是否已实际更改(即已进行调用)。有两种版本,一种是不带参数的版本,一种是带参数的版本,Object以防您想要在通知中传递一些额外的信息。在内部发生的事情是,它仅遍历Observer实例列表并update(Observable o, Object arg)为每个实例调用方法。这告诉Observer哪个是已更改的Observable对象(您可能会观察多个),还有一个Object arg可能携带一些额外信息的传递对象(通过)notifyObservers()


37

定义

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

例子

  1. 假设您的永久地址已更改,那么您需要通知护照授权机构和Pan Card授权机构。因此,这里的护照机构和泛卡机构是观察员,而您是一个主题。

  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步

创建观察者类。

观察者

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

}

第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


很好地解释了:)
roottraveller

3
我认为“定义”是一个错字。我希望这是一个错字。
JohnJohn '16

10

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

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


我将不胜感激,如果您board.changeMessage("More Homework!");在答案中添加的解释,我的意思是changeMessage("More Homework!");调用时会发生什么。
拉维2012年

9

Observer aka回调已在Observable注册。

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

在Swing中,您可以找到类似addXXXListener(Listener l)的方法,在GWT中,您可以拥有(Async)回调。

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


9

如果访问者要求不使用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);
  }

}

5

“我试图弄清楚,为什么我们确切需要观察者和可观察者”

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

一个可能有用的示例应用程序是数据绑定,假设您有一些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


3

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

帖子摘录:

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

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

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


0

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.