Java MVC上特定字段的观察者模式

Java MVC上特定字段的观察者模式,java,model-view-controller,listener,observer-pattern,Java,Model View Controller,Listener,Observer Pattern,在MVC模式上,这是模型通知视图的最佳选项(如果这首先是正确的方法),其中,从模型存储的所有数据字段中,只有几个字段被更新。特别是当我们只想更新视图的特定字段时 我目前正在使用一种MVC模式,其中包含Observer/Subscriber(JAVA Swing),如下所述:但是当模型更新时,它会更改视图中的所有内容。当调用update()函数时,无法确定模型中的哪个字段发生了更改,以便只更新视图中所需的字段 我读了这个主题:我认为它很有用,但对于后面的主题,我不太明白如何在变量(int、floa

在MVC模式上,这是模型通知视图的最佳选项(如果这首先是正确的方法),其中,从模型存储的所有数据字段中,只有几个字段被更新。特别是当我们只想更新视图的特定字段时

我目前正在使用一种MVC模式,其中包含Observer/Subscriber(JAVA Swing),如下所述:但是当模型更新时,它会更改视图中的所有内容。当调用
update()
函数时,无法确定模型中的哪个字段发生了更改,以便只更新视图中所需的字段

我读了这个主题:我认为它很有用,但对于后面的主题,我不太明白如何在变量(int、float等)上设置propertyChangeListener。与此相关的还有:

软件开始运行的主类:

public class Main {
    public static void main(String[] args) {
        Model m = new Model();
        View v = new View(m);
        Controller c = new Controller(m, v);
        c.initController();
    }
}
所以我在模型上的代码是:

public class Model extends Observable {
   //...
   private float speed;
   private int batteryPercentage;

   public float getSpeed() {
       return speed;
   }
   public void setSpeed(float speed) {
       this.speed = speed;
       setChanged();
       notifyObservers();
   }

    public int getBatteryPercentage() {
        return batteryPercentage;
    }
    public void setBatteryPercentage(int batteryPercentage) {
        this.batteryPercentage = batteryPercentage;
        setChanged();
        notifyObservers();
    }
}
视图了解模型:

public class View implements Observer {
    private Model model;
    private JTextField txtFldSpeed;
    private JTextField txtFldBattery;
    private JFrame mainWindow;

    public View(Model m) {
        this.model = m;
        initialize();
    }
    private void initialize() {
        mainWindow = new JFrame();
        mainWindow.setTitle("New Window");
        mainWindow.setMinimumSize(new Dimension(1280, 720));
        mainWindow.setBounds(100, 100, 1280, 720);
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel tPanel1 = new JPanel();
        tPanel1.setBorder(new LineBorder(new Color(0, 0, 0)));
        tPanel1.setLayout(null);
        mainWindow.getContentPane().add(tPanel1);

        mainWindow.getContentPane().add(tPanel1);
        txtFldSpeed = new JTextField();
        txtFldSpeed.setEditable(false);
        txtFldSpeed.setBounds(182, 11, 116, 22);
        tPanel1.add(txtFldSpeed);

        txtFldBattery = new JTextField();
        txtFldBattery.setEditable(false);
        txtFldBattery.setBounds(182, 43, 116, 22);
        tPanel1.add(txtFldBattery);

        mainWindow.setVisible(true);
    }
    @Override
    public void update(Observable o, Object arg) {
        txtFldSpeed.setText(Float.toString(model.getSpeed()) + " kn");
        txtFldBattery.setText(Integer.toString(model.getBatteryPercentage()) + " %");
    }
}
public class Controller {
    private Model model;
    private View view;

    public Controller(Model m, View v) {
        this.model = m;
        this.view = v;
    }

    public void initController() {    
        model.addObserver(view);
        model.setSpeed(10);
    }
}
控制器将视图添加为模型的观察者:

public class View implements Observer {
    private Model model;
    private JTextField txtFldSpeed;
    private JTextField txtFldBattery;
    private JFrame mainWindow;

    public View(Model m) {
        this.model = m;
        initialize();
    }
    private void initialize() {
        mainWindow = new JFrame();
        mainWindow.setTitle("New Window");
        mainWindow.setMinimumSize(new Dimension(1280, 720));
        mainWindow.setBounds(100, 100, 1280, 720);
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel tPanel1 = new JPanel();
        tPanel1.setBorder(new LineBorder(new Color(0, 0, 0)));
        tPanel1.setLayout(null);
        mainWindow.getContentPane().add(tPanel1);

        mainWindow.getContentPane().add(tPanel1);
        txtFldSpeed = new JTextField();
        txtFldSpeed.setEditable(false);
        txtFldSpeed.setBounds(182, 11, 116, 22);
        tPanel1.add(txtFldSpeed);

        txtFldBattery = new JTextField();
        txtFldBattery.setEditable(false);
        txtFldBattery.setBounds(182, 43, 116, 22);
        tPanel1.add(txtFldBattery);

        mainWindow.setVisible(true);
    }
    @Override
    public void update(Observable o, Object arg) {
        txtFldSpeed.setText(Float.toString(model.getSpeed()) + " kn");
        txtFldBattery.setText(Integer.toString(model.getBatteryPercentage()) + " %");
    }
}
public class Controller {
    private Model model;
    private View view;

    public Controller(Model m, View v) {
        this.model = m;
        this.view = v;
    }

    public void initController() {    
        model.addObserver(view);
        model.setSpeed(10);
    }
}
我所期望的是,当模型更新时,比方说调用函数
setSpeed()
,视图会被告知需要在特定字段上更新自己,而不是在每个“可更改”字段上更新自己(如
txtFldBattery


我之所以要这样做,是因为在视图中,有字段每秒更新几次,而且因为我需要更新视图中的所有内容,所以不需要经常更新的
JComboBox
在尝试选择选项时会一直关闭。

更新
方法实现中,您可以使用fi确定第一个参数
o
哪个可观察对象已更改,第二个参数
arg
调用时哪个值更改:
notifyobservators(this.speed);


请注意,NotifyObservators的签名接受
对象
,并且
浮点
原语不是
对象

的子类。我将使用SwingPropertyChangeSupport,使模型的每个状态字段成为“绑定属性”,以便可以分别侦听每个状态字段

例如,假设您有一个类似于以下类型的模型:

public class MvcModel {
    public static final String SPEED = "speed";
    public static final String BATTERY = "battery";
    public static final int MAX_SPEED = 40;
    private float speed;
    private int batteryPercentage;
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);

    public float getSpeed() {
        return speed;
    }

    public void setSpeed(float speed) {
        float oldValue = this.speed;
        float newValue = speed;
        this.speed = speed;
        pcSupport.firePropertyChange(SPEED, oldValue, newValue);
    }

    public int getBatteryPercentage() {
        return batteryPercentage;
    }

    public void setBatteryPercentage(int batteryPercentage) {
        int oldValue = this.batteryPercentage;
        int newValue = batteryPercentage;
        this.batteryPercentage = batteryPercentage;
        pcSupport.firePropertyChange(BATTERY, oldValue, newValue);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(name, listener);
    }

}
speed和batteryPercent字段都是“绑定字段”,因为对这些字段的任何更改都将触发property change support对象向已向支持对象注册的任何侦听器发出通知消息,如
public void setXxxx(…)
方法所示

通过这种方式,控制器可以在模型上为其想要侦听的任何属性注册侦听器,然后通知视图任何更改。例如:

class SpeedListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        float speed = model.getSpeed();
        view.setSpeed(speed);
    }
}
设置可能类似于:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class MVC2 {

    private static void createAndShowGui() {
        MvcModel model = new MvcModel();
        MvcView view = new MvcView();
        MvcController controller = new MvcController(model, view);
        controller.init();

        JFrame frame = new JFrame("MVC2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(view.getMainDisplay());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}



旁注:Observer和Observable在最新版本的Java中已被弃用,因此应该避免使用它们。

请澄清您的问题。问题越具体,发布的代码越好,通常问题的质量越高,答案越好。旁注:通常是视图已将侦听器添加到模型中,模型更改时会触发该侦听器。您可以使用一个侦听器,只需更新模型更改的整个视图,也可以使用多个侦听器,更有选择性地更新内容,这听起来就像您正在尝试做的事情。任何解决方案的详细信息都将取决于您的代码和问题的详细信息lem.谢谢你的帮助@HoverCraftfullOfels。我会用一些代码更新。我不认为它会有用。我不认为我以前被称为“Ho”,但我被称为更糟(严重)。不过,如果可能的话,请再次改进你的问题。不是你的投票人(还没有),等待您的答复update@HovercraftFullOfEels,抱歉!:-D我试图引用你。我更新了帖子。我省略了一些代码,以便于阅读,因为变量的命名非常简单。谢谢。嗨@Sal。在我的实现中,我只有一个可观察的,所以第一个参数不需要。但是关于第二个参数,我做了不完全理解,所以我知道float不是Object的子类,但在这种情况下它没有用处,因为它实际上没有告诉您哪个字段/值已经更改,而是类/实例(?)如果适用。一种可能的解决方案是,让一个对象具有一个属性字符串,该属性字符串在每次NotifyObservators调用之前定义,并将该对象传递给读取字符串并标识字段的观察者。但它似乎是硬编码的。只需使用相等运算符
==
,它就会比较对象引用。
如果(this.model.speed==arg)
考虑得好!我会研究这个问题并尽快向您更新。谢谢。您好,再次告诉您,在Java 9中,观测者模式已被弃用,尽管我感谢您的帮助,但我将进一步调查@Hovercraft Full of Eels的答案。您有我的投票权!:-)您好!这实际上与我后来发现的非常接近。我将详细查看您的答案,测试它,并尽快回复您。我也将相应地更新帖子。投票结果;-)谢谢!你好!我接受了这个答案。只有一个问题,虽然我理解SwingPropertyChangeSupport和PropertyChangeSupport之间的区别,但我为什么或者什么时候应该使用第一个?@Fred:SwingPropertyChangeSupport保证在事件调度线程上触发通知,并且只在这个线程上,因此应该用于Swing GUI通知。请注意,所有Swing组件都已将其中一个对象作为类的字段保存。非常感谢!祝你复活节愉快(如果你庆祝的话)!