观察者模式 - 观察者是否需要知道主题?

时间:2015-10-05 05:41:22

标签: java design-patterns observer-pattern

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

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

我几乎得到了这个想法,但有一件事困扰我:Observer有一个setSubject()方法,他自己(观察者)也注册了主题并设置了主题,这在我看来有点有点不对劲

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

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

嗯,观察者必须知道有关他的主题的细节,否则他将无法观察它,对吧?与现实生活相匹配:您还必须了解您需要采取特定行动时所了解的对象的具体信息,对吗?

问题是,你可以将它与接口分离,这样你就可以构建一个更通用的观察者...然后你的观察对象只需要实现那个观察接口你的观察者准备好了。

答案 2 :(得分:1)

让我们看一下1995年UML中由GoF定义的观察者模式:

Observer GoF

ConcreteObserversubjectConcreteSubject的链接 - 该模式未指定您如何设置它;它可以在构造函数中注入,也可以使用setSubject()方法注入。

Subject方面,您的代码有registerunregister,类似于AttachDetach,以及您的notifyObservers也适合这种模式。

  

从Observer模式的角度来看,我是否还需要在注册后设置主题?

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

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

取决于趋于稳定的事情

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

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