繁体   English   中英

观察者模式-观察者是否需要了解主题?

[英]Observer pattern - does the observer needs to know the Subject ?

我试图了解观察者模式,这是我在网上选择的一个简单代码:

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()方法,他本人(观察者)也注册了subject并设置了Subject,在我看来这有点不对劲。

我可以更改接口并删除setSubject()方法,但是我的问题是从Observer模式的角度来看,我真的还需要在注册后设置一个主题吗?

没错,由于此行,此方法添加了不必要的依赖性:

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

可以删除这两个方法( setSubjectgetUpdate ),并且可以从update()直接检索msg ,如下所示:

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

好吧,观察者必须知道有关他的主题的细节,否则他将无法观察它,对吗? 与现实生活相匹配:您还必须了解正在观察的对象的那些细节,以便在何时需要采取特定措施时知道对不对?

关键是, 您可以将其与interfaces分离 ,因此您可以构建一个更通用的观察者...然后,您所观察的对象只需要实现该观察接口,观察者就可以使用了。

让我们看看1995年UML中GoF定义的Observer模式:

观察员

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的值,因为它具有更多的分辨率来知道电池可持续使用多长时间,则您必须更改所有观察者的代码(它们取决于主题)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM