【问题标题】:Observer pattern - does the observer needs to know the Subject ?观察者模式 - 观察者需要知道主题吗?
【发布时间】:2015-10-05 05:41:22
【问题描述】:

我正在尝试理解观察者模式,这是我在网上挑选的一个简单代码:

import java.util.ArrayList;
import java.util.List;


/**
 * The observer pattern is a software design pattern in which an object, 
 * called the subject, maintains a list of its dependents, called observers, and 
 * notifies them automatically of any state changes, usually by calling one of their methods
 * @author X220
 *
 */


interface Subject 
{
    // methods to register and unregister observers
    public void register(Observer obj);

    public void unregister(Observer obj);

    // method to notify observers of change
    public void notifyObservers();

    // method to get updates from subject
    public Object getUpdate(Observer obj);
}

interface Observer 
{
    // method to update the observer, used by subject
    public void update();

    // attach with subject to observe
    public void setSubject(Subject sub);
}

/**
 * Holds a list of the observers
 * @author X220
 *
 */
class MySubject implements Subject 
{
    private List<Observer> observers;
    private String message;
    private boolean changed;
    private final Object MUTEX = new Object();

    public MySubject() 
    {
        this.observers = new ArrayList<>();
    }

    @Override
    public void register(Observer obj) 
    {
        if (obj == null)
            throw new NullPointerException("Null Observer");
        synchronized (MUTEX) {
            if (!observers.contains(obj))
                observers.add(obj);
        }
    }

    @Override
    public void unregister(Observer obj) 
    {
        synchronized (MUTEX) 
        {
            observers.remove(obj);
        }
    }

    @Override
    public void notifyObservers() 
    {
        List<Observer> observersLocal = null;
        // synchronization is used to make sure any observer registered after
        // message is received is not notified
        synchronized (MUTEX) 
        {
            if (!changed)
            {
                return;
            }
            observersLocal = new ArrayList<>(this.observers);
            this.changed = false;
        }
        for (Observer obj : observersLocal) 
        {
            obj.update();
        }
    }

    @Override
    public Object getUpdate(Observer obj) {
        return this.message;
    }

    // method to post message to the topic
    public void postMessage(String msg) {
        System.out.println("Message Posted to Topic:" + msg);
        this.message = msg;
        this.changed = true;
        notifyObservers();
    }

}

class MyObserver implements Observer 
{
    private String name;
    private Subject topic;

    public MyObserver(String nm) {
        this.name = nm;
    }

    @Override
    public void update() {
        String msg = (String) topic.getUpdate(this);
        if (msg == null) {
            System.out.println(name + ":: No new message");
        } else
            System.out.println(name + ":: Consuming message::" + msg);
    }

    @Override
    public void setSubject(Subject sub) {
        this.topic = sub;
    }

}


public class RunCode1 
{

    public static void main1(String[] args) {
        // create subject
        MySubject topic = new MySubject();

        // create observers
        Observer obj1 = new MyObserver("Obj1");
        Observer obj2 = new MyObserver("Obj2");
        Observer obj3 = new MyObserver("Obj3");

        // register observers to the subject
        topic.register(obj1);
        topic.register(obj2);
        topic.register(obj3);

        // attach observer to subject
        obj1.setSubject(topic);
        obj2.setSubject(topic);
        obj3.setSubject(topic);

        // check if any update is available
        obj1.update();

        // now send message to subject
        topic.postMessage("New Message");
    }
}

我差不多明白了,但有一件事困扰着我:观察者有一个setSubject() 方法,他自己(观察者)也注册到主题并设置主题,在我看来有点错误.

我可以更改界面并删除setSubject() 方法,但是我的问题是,从观察者模式的角度来看,我是否真的需要在注册后设置主题?

【问题讨论】:

  • getUpdate(Observer) 有什么用法吗?对我来说,观察者模式似乎是多余的。
  • @Spotted:我完全同意你的观点......如果观察者对它的主题有依赖性,那没关系,但主题需要对观察者不依赖......

标签: java design-patterns observer-pattern


【解决方案1】:

你是对的,这个方法增加了不必要的依赖,因为这行:

String msg = (String) topic.getUpdate(this);

这两个方法(setSubjectgetUpdate)可以去掉,msg可以直接从update()检索,像这样:

@Override
public void update(String msg) {
    if (msg == null) {
        System.out.println(name + ":: No new message");
    } else
        System.out.println(name + ":: Consuming message::" + msg);
}

随着notifyObservers的更新:

for (Observer obj : observersLocal) 
{
    obj.update(this.message);
}

【讨论】:

    【解决方案2】:

    好吧,观察者必须知道他的主题的细节,否则他将无法观察它,对吧?与现实生活相匹配:您还必须了解所观察对象的这些细节,才能知道何时需要采取特定行动,对吧?

    问题是,您可以将其与接口解耦,因此您可以构建一个更通用的观察者...然后您的观察对象只需要实现该观察接口和您的观察者准备好了。

    【讨论】:

      【解决方案3】:

      让我们看看 GoF 从 1995 年开始在 UML 中定义的观察者模式:

      ConcreteObserver 有一个subjectConcreteSubject 的链接——该模式没有指定你如何设置它;它可以像您所做的那样注入到构造函数中或使用setSubject() 方法。

      Subject 方面,您的代码具有registerunregister,类似于AttachDetach,您的notifyObservers 也符合该模式。

      从观察者模式的角度来看,我真的需要在注册后设置一个主题吗?

      根据这个 GoF 版本的模式,ConcreteObserver 必须知道主题才能更新自己。所以,你需要设置一个主题。

      有一个模式的变体(Java 的Observer/Observable),其中Update() 方法传递一个包含状态相关信息的对象,这样ConcreteObserver 可以更新其状态而无需通信与ConcreteSubject。理论上,您不需要存储指向该主题的链接。

      依赖于趋于稳定的事物

      该模式的目标不是消除所有依赖关系。在软件开发中,有一种趋势是希望添加新的观察者(想想 Microsoft Word 的 GUI 版本有多少)。主题是倾向于没有代码更改的类。软件设计中的一条规则是对不太可能对其代码进行更改的类创建依赖项。观察者模式是说ConcreteObservers 往往会发生变化(随着时间的推移会有新版本),因此我们不希望也必须更改ConcreteSubjects

      如果您更改主题的代码,该模式将无济于事。例如,让我们以一个代表手机电池剩余电量的主题为例,它的值在 0 到 10 之间。一些观察者使用图形或颜色显示该信息;也许当值低于 2 时,观察者会显示红色图像。现在,如果开发人员决定最好显示 0 到 100 之间的值,因为它有更多的分辨率来了解电池的持续时间,您将不得不更改所有观察者的代码(它们取决于主题)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-25
        • 1970-01-01
        • 1970-01-01
        • 2016-02-20
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        相关资源
        最近更新 更多