我正在研究用Java实现的观察者模式,我对教程实现的确切运作方式有些怀疑。
我知道这种模式用于期望用户与GUI交互的所有情况(例如用户点击按钮)或者在不可预测的时间发生事件的所有情况(如计时器,当计时器捕捉时必须处理一个事件。)
我知道在观察者模式中涉及两种不同类型的对象:
主题对象:在不可预测的时间发生某事的对象(例如,可以随时点击的按钮用户)
观察者对象(或侦听器):在我看来,它包含当某些内容更改为主题<时所执行的代码/ strong>对象(例如它处理我的按钮的单击)。这是真的还是我错过了什么?对于这个陈述,我绝对不是这样......我怀疑观察者对象只能听取主题的变化并对主题执行操作当这种变化发生时。例如:单击按钮(主题),观察者听取它并发现此更改,并对主题执行特定操作(对主题对象执行特定方法)。
正确的解释是什么?观察者听取主题变化并执行在观察者中定义的操作或执行在主体中定义的操作吗?
我知道要这样做我可以将主题的引用传递给观察者(我认为已执行的操作已定义到主题中。)但是这样是一种不良做法,因为它涉及主题和观察者之间的紧密耦合形式。我知道存在一个界面驱动的解决方案,但我很难搞清楚它。
本教程使用此示例表示使用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个按钮代表我的主题对象,其中包括:
private JButton helloButton;
private JButton goodbyeButton;
View 类实现了一个 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()方法已在视图类中实现,因为它扩展了 ActionListener 接口并处理了按钮的单击。
ActionListener 界面实现观察者对象?这意味着 View *类包含**主题对象(我的按钮),但它也是观察者/监听器实现?
要将 listerner (观察者对象)添加到我执行的按钮中:
helloButton.addActionListener(this);
我传递 this ,因为包含观察者的类与定义主题对象的类相同。这是对的吗?
这是一个最佳实践,因为我将主题与观察者捆绑在一起进入一个类,并且我没有通过观察者内部主题的引用得到的紧耦合?
我的推理是正确还是我错过了什么?
答案 0 :(得分:3)
让我们从&#34;理论&#34;开始背景。首先,人们必须了解观察者的模式;以及&#34; Java&#34;通常是为你提供它&#34;已经存在了很长一段时间&#34;。稍后会有细微的变化,但我认为可以说我们正在讨论九十年代后期的概念。
我的建议是:不要只阅读这种模式的初始形式(如着名的四人帮&34;设计原则&#34;书中所述);但也请查看Reactive Programming对此有何评论。
所以,我所说的是:在2015年你仍然可以坐下来做你的Java Swing应用程序,就像10年或15年前那样。但是尽量不要太专注于这种做事方式;现在还有其他选择;并且根据您的应用程序的要求,旧的Java GUI事物&#34;可能不是正确的答案。
然后:那种&#34;捆绑&#34;你在最后几段中建议更多的是反模式&#34;而不是最佳做法。当然,它非常适合于#34;非常小的一次写入永不更新的类似组件。
所以,是的,当你开始学习观察者模式时;这样开始是很自然的事情。但这并不能避免紧密耦合&#34; - 使用你的方法,耦合可以变得如此紧张(我们不是在谈论同一类中的事情吗?!),几乎不可能“解耦”#34;他们以后。
一旦你谈论&#34;现实&#34;应用程序,你应该避免这种捆绑。它始于......您可能需要相同的&#34;动作&#34;单击按钮时发生...或用户转到某个菜单时。或者UI的其他部分变得更复杂;而且突然之间,&#34;不同&#34;需要为同一个按钮执行操作;或者这个东西在这里&#34;现在变得依赖于&#34;其他一些东西&#34;。
换句话说:如果您的应用程序很复杂,那么建议的方法会导致巨大的,整体的,不可维护的意大利面条代码碗变化很大。
换句话说:超越&#34;简单的运动代码,以后不需要维护&#34;不应该按照您在此处列出的模式构建。
是的,我承认这并不能回答这个隐含的问题:&#34;怎样才能做得更好&#34; ......因为这是一个艰难的。一个可能的提示可能是JavaFx&#34; best practices&#34;来自Oracle