Java观察者模式的接口驱动实现解读

Interpretation of an interface driven implementation of the observer pattern in Java

我正在研究 Java 中实现的 观察者模式 ,我对教程实现的具体工作方式有一些疑问。

我知道这种模式用于所有期望用户与 GUI 交互的情况(例如用户单击按钮)或事件在不可预测的时间发生的所有情况(例如计时器,当计时器捕捉到一个事件时必须处理)。

我知道观察者模式涉及两种不同类型的对象:

  1. 主题对象:即在不可预测的时间发生某些事情的对象(例如用户可以随时点击的按钮)

  2. observer对象(或listener):在我看来,它包含执行的代码当某些东西变成 subject 对象时(例如它处理我的按钮的点击)。是真的还是我错过了什么?我对这个说法绝对不正确......我怀疑 observer 对象可能只需要监听 subject 和当发生这种变化时,对主题执行操作。例如:按钮(subject)被点击,观察者监听并发现这个变化,并对subject执行特定的操作(perform a specific method on the subject object) .

正确的解释是什么?观察者监听主题变化并执行定义到观察者的操作或执行定义到主题的操作?

我知道要做到这一点,我可以将 subject 的引用传递给 observer(我认为执行的操作是定义到主题中。)但这是不好的做法,因为它涉及主题和观察者之间的紧耦合形式。我知道存在一个接口驱动的解决方案,但我在弄清楚它时遇到了一些困难。

本教程制作的这个示例表示使用 Swing 呈现 2 个按钮的视图:

package com.caveofprogramming.designpatterns.demo1.view;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

import com.caveofprogramming.designpatterns.demo1.model.Model;

public class View extends JFrame implements ActionListener {

    private Model model;

    private JButton helloButton;        // SUBJECT OBJECT: l'oggetto su cui avviene l'evento
    private JButton goodbyeButton;      // SUBJECT OBJECT: l'oggetto su cui avviene l'evento

    public View(Model model) {
        super("MVC Demo");

        this.model = model;

        // Crea i 2 bottoni definiti sopra:
        helloButton = new JButton("Hello!");
        goodbyeButton = new JButton("Goodbye!");

        // Setta il layout manager da usare che stabilisce come sono posizionati gli elementi:
        setLayout(new GridBagLayout());

        GridBagConstraints gc = new GridBagConstraints();
        gc.anchor = GridBagConstraints.CENTER;
        gc.gridx=1;
        gc.gridy=1;
        gc.weightx=1;
        gc.weighty=1;
        gc.fill=GridBagConstraints.NONE;

        add(helloButton, gc);   // Aggiunge helloButton dentro il layout

        gc.anchor = GridBagConstraints.CENTER;
        gc.gridx=1;
        gc.gridy=2;
        gc.weightx=1;
        gc.weighty=1;
        gc.fill=GridBagConstraints.NONE;

        add(goodbyeButton, gc); // Aggiunge goodbyeButton dentro il layout

        helloButton.addActionListener(this);
        goodbyeButton.addActionListener(this);

        goodbyeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Sorry to see you go.");
            }

        });

        setSize(600, 500);                          // Setta le dimensioni della finestra
        setDefaultCloseOperation(EXIT_ON_CLOSE);    // Setta il comportamento di cosa succede premendo il tasto X
        setVisible(true);                           // Setta la finestra come visibile
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        JButton source = (JButton)e.getSource();

        if(source == helloButton) {
            System.out.println("Hello there!");
        }
        else {
            System.out.println("Some other button.");
        }

    }   

}

在这个例子中,我有 2 个按钮代表我的 subject 对象,这些:

private JButton helloButton;        
private JButton goodbyeButton;

Viewclass实现了一个ActionListener接口(就是前面说的那个接口?)

查看 ActionListener.class 我发现:

// Method descriptor #8 (Ljava/awt/event/ActionEvent;)V
public abstract void actionPerformed(java.awt.event.ActionEvent arg0);

这是一个必须实现的方法,这是当一个动作发生时调用的方法,我必须实现它,这个:

@Override
public void actionPerformed(ActionEvent e) {

    JButton source = (JButton)e.getSource();

    if(source == helloButton) {
        System.out.println("Hello there!");
    }
    else {
        System.out.println("Some other button.");
    }

}

actionPerformed() 方法在 View class 中实现,因为它扩展了 ActionListener 界面,它处理按钮的点击。

ActionListener接口实现是我的observer对象吗?这意味着 View* class 包含 **subject 对象(我的按钮),但它也是一个 observer/listener实施?

要将 listernerobserver 对象)添加到我做的按钮:

helloButton.addActionListener(this);

我传递 this 因为包含 observer 的 class 与 subject 个对象被定义。正确吗?

这是最佳实践,因为我将对象与观察者捆绑在一起 class 并且我没有通过观察者内部对象的引用派生出紧密耦合?

是我的推理正确还是我遗漏了什么?

让我们从 "theoretical" 背景开始。首先,必须了解观察者模式;以及 "Java" 通常如何提供它 "to you" 已经存在了 "quite some time"。后来发生了微妙的变化,但我认为可以公平地说,我们正在谈论九十年代后期的概念。

我的建议是:不要只阅读此模式的初始形式(如著名的四人帮 "design principles" 一书中所述);但也看看 Reactive Programming 对此有何评论。

所以,我想说的是:在 2015 年,您仍然可以像 10 或 15 年前那样坐下来做 Java Swing 应用程序。但是尽量不要太专注于这种做事方式;现在有其他选择;根据您的应用程序要求,"old school Java GUI thing" 可能不是正确答案。

那么:您在最后几段中建议的那种 "bundling" 更像是 "anti-pattern" 而不是最佳实践。当然,它适用于 "very small write-once never-update" 类组件。

所以,是的,当你开始学习观察者模式时;这样开始是很自然的事情。但这不是避免 "close coupling" 的答案 - 使用您的方法,耦合会变得如此紧密(我们不是在谈论相同 class 中的东西吗?!)几乎不可能"de-couple"他们稍后。

一旦您谈论 "realistic" 应用程序,您就应该避免这种捆绑。它始于这样一个事实......您可能希望在单击按钮时发生相同的 "action" ......或者当用户转向某个菜单时。或者你的 UI 的其他部分变得更复杂;突然之间,"different" 需要对同一个按钮执行操作;或者 "this thingy here" 的状态现在取决于 "some other thingy over there" 的状态。

换句话说:如果您的应用程序很复杂,那么建议的方法会导致巨大的、单一的、不可维护的意大利面条代码碗。

换句话说:"simple exercise code that doesn't need maintenance in the future" 之外的任何内容都不应按照您在此处概述的模式构建。

是的,我承认这没有回答隐含的问题:"how can it be done better" ... 因为这是一个棘手的问题。一个潜在的提示可能是来自 Oracle

的 JavaFx“best practices