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



一位面试官问我:

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

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

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

2)Observable类维护Observers的列表。

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

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

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

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

用非常简单的术语来说(因为其他答案都是指所有官方设计模式,所以请查看它们以了解更多详细信息):

如果你想要一个由程序生态系统中的其他类监控的类,你会说你希望这个类是可观察的。也就是说,它的状态可能会有一些变化,你想把这些变化广播给节目的其他部分。

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

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

最后要解决的问题是Observable类如何知道谁感兴趣?因此,Observable类必须提供一些机制,允许观察者注册他们的兴趣。像addObserver(Observer o)这样的方法在内部将Observer添加到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().

定义

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

示例

  1. 假设您的永久地址发生了更改,那么您需要通知护照管理局和泛卡管理局。所以这里的护照管理局和泛卡管理局是观察员,你是一个主体。

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

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和具体的observer对象。

Observer PatternMo.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

十六进制字符串:

八进制字符串:12

二进制字符串:1010

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

当使用这个模式时,您将两个实体彼此解耦——观察者变成可插入的。

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

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

在Swing中可以找到addXXXListener(Listener l)等方法,在GWT中可以找到(Async)回调。

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

如果面试官要求在不使用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);
}
}

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

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

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

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

我还在VisualStudio2008的起始页上找到了这篇文章(观察者模式是模型-视图-控制器(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

文章摘录:

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

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

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

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

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

然而,你可能仍然会收到关于他们的面试问题。。。

最新更新