java.util.observer 有多个 JFrame

java.util.observer with multiple JFrame

我正在尝试使用多个 JFrame 实例实现观察者模式。但是,调用 notifyObservers() 时,只会更新最后实例化的 JFrame 实例。

这是我的代码:

Mall.java

    import java.util.ArrayList;
    import java.util.Observable;

    public class Mall extends Observable{
        private ArrayList<String> stores;

        public Mall(){
           stores = new ArrayList<String>();
        }

        public void addNewStore(String store){
           stores.add(store);
           System.out.println(store);
           setChanged();
           notifyObservers(store);
        }

        public ArrayList<String> getStores(){
            return stores;
        }
    }

CustomerFrame.java

    public class CustomerFrame extends javax.swing.JFrame {

        public CustomerFrame() {
           initComponents();
        }

        public CustomerFrame(Mall theMall) {
            initComponents();
            MallObserver mallObs = new MallObserver();
            theMall.addObserver(mallObs);

        }

        private void initComponents() {

            jScrollPane1 = new javax.swing.JScrollPane();
            jList1 = new javax.swing.JList();

            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

            jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
            jScrollPane1.setViewportView(jList1);

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 249, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(141, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(124, Short.MAX_VALUE))
            );

            pack();
        }                   

        public static javax.swing.JList jList1;
        private javax.swing.JScrollPane jScrollPane1;                  
    }

MallObserver.java

    import java.util.Observable;
    import java.util.Observer;
    import javax.swing.DefaultListModel;

    public class MallObserver implements Observer{

        private Mall mallUpdate;

        @Override
        public void update(Observable o, Object arg) {
           mallUpdate = (Mall) o;
           DefaultListModel listModel = new DefaultListModel();
           for(int i = 0; i < mallUpdate.getStores().size(); i++)
               listModel.addElement(mallUpdate.getStores().get(i));
           CustomerFrame.jList1.setModel(listModel);
        }

    }

AdminFrame.java

        public class AdminFrame extends javax.swing.JFrame {

        Mall theMall;

        public AdminFrame() {
            initComponents();
            theMall = new Mall();
        }
    @SuppressWarnings("unchecked")                         
        private void initComponents() {

            jTextField1 = new javax.swing.JTextField();
            jLabel1 = new javax.swing.JLabel();
            jButton1 = new javax.swing.JButton();
            jButton2 = new javax.swing.JButton();

            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

            jTextField1.setText("jTextField1");

            jLabel1.setText("jLabel1");

            jButton1.setText("Add Store");
            jButton1.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jButton1ActionPerformed(evt);
                }
            });

            jButton2.setText("New Customer");
            jButton2.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jButton2ActionPerformed(evt);
                }
            });

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addContainerGap(143, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jButton2)
                .addComponent(jButton1)
                .addGroup(layout.createSequentialGroup()
                    .addComponent(jLabel1)
                    .addGap(26, 26, 26)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
            .addGap(138, 138, 138))
            );
            layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(129, 129, 129)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(jLabel1))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jButton1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(jButton2)
            .addContainerGap(88, Short.MAX_VALUE))
            );

            pack();
        }

     private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                         
            CustomerFrame newCust = new CustomerFrame();
            newCust.setVisible(true);
        } 

     private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
           theMall.addNewStore(jTextField1.getText());
        }

     public static void main(String args[]) {

            try {
                for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (InstantiationException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                        java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                    }

     java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new AdminFrame().setVisible(true);
                }
            });
        }


        private javax.swing.JButton jButton1;
        private javax.swing.JButton jButton2;
        private javax.swing.JLabel jLabel1;
        private javax.swing.JTextField jTextField1;

    }                

思路是,当顾客进入商店时,商店会为顾客打开一个新的JFrame。当管理员添加商店时,所有 JFrame CustomerFrame.java 都将更新其列表项。但是,在我的例子中,只有最后实例化的 CustomerFrame.java 会得到更新,而其他的保持不变。

可以通过打开 2 个新客户并尝试在 AdminFrame.java

添加商店来重现上述问题

这里有什么问题?

您没有从 MallCustomer 的 update(...) 方法中调用 thisFrame 上的任何方法。而不是在 storeList 上设置模型(那到底是从哪里来的?),给 CustomerFrame 一个 public 方法,比如说你在 update 中调用的 setStoreListModel(ListModel listModel),传递在商店模型中。

public class CustomerFrame extends javax.swing.JFrame {
    privarte Mall theMall;
    private JList storesList = new JList();

    public CustomerFrame(Customer customer, Mall theMall){
        MallCustomer mallObserver = new MallCustomer(this);
        theMall.addObserver(mallObserver);
    }

    public setStoresListModel(ListModel listModel) {
        storesList.setModel(listModel);
    }
}

@Override
public void update(Observable o, Object arg) {
    mallUpdate = (Mall) o;
    DefaultListModel storeModel = new DefaultListModel();

    //Stores update
    for(int i = 0; i < mallUpdate.getStores().size();i++) {
        storeModel.addElement(mallUpdate.getStores().get(i));
    }
    // storesList.setModel(storeModel);//a JList variable
    thisFrame.setStoresListModel(storeModel);
}    

注意:代码未编译也未测试


编辑

我看到了几个问题:

  • 您的 JList 不应 public 也不应是静态的。将其设为私有实例字段。
  • 再次(正如我一直建议的那样),为客户 window 提供一个 public setListModel 类型的方法。
  • 一个应用程序应该只有一个主 JFrame,因此 CustomerFrame JFrame 不应该是一个 JFrame,而应该是一个非模态 JDialog,或者更好的是一个可以放置在任何地方的 JPanel -- 在它自己的 JDialog 中,在主 JFrame 中。
  • 将主 JFrame 传递到您的 JDialog 中,以便对话框可以在其超类的构造函数中注册父 JFrame。
  • 将 CustomerDialog 实例传递到您的 MallObserver 实例中,然后使用它在 MallObserver 中设置一个字段。
  • 在更新方法中,创建或更新模型并在 MallObserver 持有的客户对话实例上调用 `setListModel。
  • 在 调用 initComponents() 之前创建商城实例 。这样您就可以在您的动作侦听器方法中使用相同的 Mall 实例。
  • 挑剔:在创建和 posting MCVE 时,摆脱 NetBeans 生成的混乱和分散注意力的代码。取而代之的是您自己创建的 post 简单代码和简单 GUI,类似于我在下面所做的更改。
  • 你的 MCVE 应该都放在一个文件中。该文件可以有几个类,但我们应该很容易剪切和粘贴到我们的IDE,然后是运行。

例如:

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.awt.event.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class AdminFrame extends javax.swing.JFrame {

    private Mall theMall = new Mall(); //!!

    public AdminFrame() {
        initComponents();
        //!! theMall = new Mall();
    }

    private void initComponents() {

        jTextField1 = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        addStoreBtn = new javax.swing.JButton();
        newCustBtn = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTextField1.setText("jTextField1");

        jLabel1.setText("jLabel1");

        addStoreBtn.setText("Add Store");
        addStoreBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                theMall.addNewStore(jTextField1.getText());
            }
        });
        addStoreBtn.setMnemonic(KeyEvent.VK_S);

        newCustBtn.setText("New Customer");
        newCustBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                // !! CustomerFrame newCust = new CustomerFrame();
                CustomerDialog newCust = new CustomerDialog(AdminFrame.this, theMall);
                newCust.pack();
                newCust.setLocationByPlatform(true);
                newCust.setVisible(true);
            }
        });
        newCustBtn.setMnemonic(KeyEvent.VK_C);

        JPanel mainPanel = new JPanel();
        mainPanel.add(jLabel1);
        mainPanel.add(jTextField1);
        mainPanel.add(addStoreBtn);
        mainPanel.add(newCustBtn);

        add(mainPanel);

        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String args[]) {

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new AdminFrame().setVisible(true);
            }
        });
    }

    private javax.swing.JButton addStoreBtn;
    private javax.swing.JButton newCustBtn;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JTextField jTextField1;

}

class MallObserver implements Observer {

    private Mall mallUpdate;
    private CustomerDialog customerDialog; // !!

    // !!
    public MallObserver(CustomerDialog customerFrame) {
        this.customerDialog = customerFrame; // !!
    }

    @Override
    public void update(Observable o, Object arg) {
        mallUpdate = (Mall) o;
        DefaultListModel<String> listModel = new DefaultListModel<>();
        for (int i = 0; i < mallUpdate.getStores().size(); i++) {
            listModel.addElement(mallUpdate.getStores().get(i));
        }

        customerDialog.setListModel(listModel);
    }

}

@SuppressWarnings("serial")
class CustomerDialog extends JDialog {  //!!

    // !!!!!!! public CustomerFrame() {
    // initComponents();
    // }

    public void setListModel(ListModel<String> listModel) {
        jList1.setModel(listModel);
    }

    public CustomerDialog(AdminFrame adminFrame, Mall theMall) {
        super(adminFrame, "Customer Dialog", ModalityType.MODELESS);
        initComponents();
        // !! MallObserver mallObs = new MallObserver();
        MallObserver mallObs = new MallObserver(this); // !!
        theMall.addObserver(mallObs);
    }

    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new JList<>();
        jList1.setPrototypeCellValue("                               ");
        jList1.setVisibleRowCount(15);

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane1.setViewportView(jList1);

        add(jScrollPane1);

        pack();
    }

    // public static javax.swing.JList jList1;
    private JList<String> jList1;
    private JScrollPane jScrollPane1;
}

class Mall extends Observable {
    private ArrayList<String> stores;

    public Mall() {
        stores = new ArrayList<String>();
    }

    public void addNewStore(String store) {
        stores.add(store);
        setChanged();
        notifyObservers(store);
    }

    public ArrayList<String> getStores() {
        return stores;
    }


}