java对象间通信

时间:2014-08-02 20:27:59

标签: java swing oop design-patterns

还在学习Java。

Swing再一次让我问这个,但这确实是一个普遍的OO问题。如果我有一个主类(包含main())它创建一个新对象“A”,它做了一些事情,主类现在有一个对该对象的引用,对象“B”如何访问该对象的属性?

我能想到的唯一方法是让master类创建一个新对象“B”,将对象“A”作为参数传递给构造函数,我想这是O.K.但这不会使事件处理变得困难。

例如,也许这是一个导致问题的糟糕设计。我有一个带有程序逻辑的主类,它创建一个标准的Swing框架,带有菜单,菜单项有动作监听器。但是actionlistener需要与外部对象进行交互。

所以有些代码(忽略细节):

主类,包含程序逻辑以及save和load方法等:

public final class TheProgramme implements WindowListener }
    private static final TheProgramme TP = new TheProgramme();
    // Declare Class variables, instance variables etc.

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShewGUI();
            }
        });
    }

    private static void createAndShewGUI() {
        TP.populateAndShew();
    }

    private void populateAndShew() {
        final StandardFrame sF = new StandardFrame("TheProgramme");
        theFrame = sF.getMainFrame();
        theFrame.addWindowListener(this);
        theFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        theFrame.pack(); theFrame.setVisible(true);
    }
...
}

所以我们创建了一个标准的框架对象,它创建了一个菜单,空面板和状态栏,但菜单项上有事件监听器:

public class StandardFrame {
    // Declare instance variables that must be visible to the ActionListener inner class

    public StandardFrame(String theTitle) {
        mainFrame = new JFrame(theTitle);
        mainFrame.setJMenuBar(createMenuBar()); // ... the menu bar and ...
        mainFrame.setContentPane(createBlankPanel()); // ... a blank panel
        java.net.URL imageURL = TheProgramme.class.getResource("images/icon.png");
        if (imageURL != null) {
            ImageIcon icon = new ImageIcon(imageURL);
            mainFrame.setIconImage(icon.getImage());
        }
    }

    public JMenuBar createMenuBar() {
        ActionListener menuEvents = new MenuListener();
        JMenuBar aMenuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F);
        ...
        aMenuBar.add(fileMenu);
        ...
        JMenuItem newItem = new JMenuItem("New", KeyEvent.VK_N); newItem.addActionListener(menuEvents);
        newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
        ...
        fileMenu.add(newItem);
        ...
        return aMenuBar;
    }
}

class MenuListener implements ActionListener {

    public void actionPerformed(ActionEvent ae) {
        String actionCommand = ae.getActionCommand();
        switch (actionCommand) {
            case "New":
                // !!! here we need to call a method in an object to create a new document object !!!
                break;
             case "Reformat":
                // !!! here we need to call a method in the object created above
         }
     }
}

第一个问题是菜单项上的actionlistener调用了一个创建对象的方法,但它不是对静态方法的调用。 第二个问题是它需要能够在以后通过另一个菜单选择调用该新对象中的方法。

4 个答案:

答案 0 :(得分:3)

一般来说,(并且很难知道这是否是您问题的答案,因为它非常模糊)这是setModel(...)addListener(...)的用途。

“构造函数协调器”又名“大师类”,创建模型(swing模型类)。它创建视图(JWidgets)并设置视图的模型。在Swing中,很容易依赖于默认构造的模型(使用JWidget默认构造函数填充),但是在您的情况下,您应该找到导致问题的一个窗口小部件并重写它使模型设置显式。

如果您扩展了Jwhatever,请记住setModel(...)通常会执行类似

的操作
if (this.model != null) {
   this.model.removeListener(this);
}
// clear the cached last "view" of the model
clearCachedData(...);
if (model != null) {
   this.model = model;
   // restore the "view" of the new model.
   grabCachedData(...);
   this.model.addListener(this);
}

答案 1 :(得分:3)

我希望我对这个问题的解释是正确的。您可以向操作实现注入/提供所需的任何对象。下面是一个使用接口进行更好抽象的示例作为actionPerformed的回调。当操作完成时,调用其回调以通知任何感兴趣的人。在这种情况下,会通知面板并使用一些文本更新其文本区域。

界面:

public interface ActionCallback {
    public void documentCreated(String name);
}

以下是用户界面:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class TestAction extends JPanel implements ActionCallback {
    private JTextArea area;
    public TestAction() {
        setLayout(new BorderLayout());
        area = new JTextArea();
        add(new JScrollPane(area));
    }

    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    public void documentCreated(String name) {
        area.append(String.format("Created %s\n", name));
    }

    public static class NewAction extends AbstractAction {
        private ActionCallback callback;
        private Component parent;

        public NewAction(ActionCallback callback, Component parent){
            super("New");
            this.callback = callback;
            this.parent = parent;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            String value = JOptionPane.showInputDialog(parent, "Name", "new name");
            if (value != null){
                callback.documentCreated(value);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {   
            public void run() {   
                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationByPlatform(true);

                TestAction panel = new TestAction();
                frame.add(panel);

                JMenuBar menuBar = new JMenuBar();
                JMenu menu = new JMenu("Menu");
                menuBar.add(menu);

                JMenuItem item = new JMenuItem(new NewAction(panel, frame));
                menu.add(item);
                frame.setJMenuBar(menuBar);

                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

答案 2 :(得分:3)

Model-View-Controller中执行此操作的经典方法是在运行时将对象绑定在一起。控制器(您的动作侦听器)接受参数以指示要对其执行的视图和模型。

这种参数的使用也被称为"依赖注入,"正如Aqua所提到的那样。

   public static void main( String[] args )
   {
      Model model = new Model();
      View view = new View();
      ActionListener listener = new MyActionListener( model, view );
      view.addActionListener( listener );
   }

   private static class MyActionListener implements ActionListener
   {
      private Model model;
      private View view;

      public MyActionListener( Model model, View view )
      {
         this.model = model;
         this.view = view;
      }
   }

在Java中你可以作弊,因为ActionEvent有一个指向事件源的指针(通常是生成事件的view / JComponent。

private static class MyActionListener implements ActionListener
{
  private Model model;

  public MyActionListener( Model model )
  {
     this.model = model;
  }

  @Override
  public void actionPerformed( ActionEvent e )
  {
     JComponent source = (JComponent) e.getSource();
     // source == "view"...
  }
}

要设置新文档,您可以创建一个"文档持有者"类。 "新"菜单项将新文档放入持有者类。所有其他菜单项" get"来自持有人类的文件。这是一个相当严格的OO范例,它不使用静态方法或字段,虽然它有点乏味。

设置:

   public static void main( String[] args )
   {
      ModelDocumentHolder model = new ModelDocumentHolder();
      View view = new View();
      ActionListener listener = new NewDocument( model );
      view.addActionListener( listener );
      View view2 = new View();
      view2.addActionListener( new RegularListener( model ) );
   }

新文档监听器:

   private static class NewDocument implements ActionListener
   {
      private ModelDocumentHolder model;

      public NewDocument( ModelDocumentHolder model )
      {
         this.model = model;
      }

      @Override
      public void actionPerformed( ActionEvent e )
      {
         model.setDoc( new Document() );
      }
   }

大多数其他菜单项:

   private static class RegularListener implements ActionListener
   {
      private ModelDocumentHolder model;

      public RegularListener( ModelDocumentHolder model )
      {
         this.model = model;
      }

      @Override
      public void actionPerformed( ActionEvent e )
      {
         JComponent source = (JComponent) e.getSource();
         Document doc = model.getDoc();
         // do stuff...
      }
   }

持有人类:

   private static class ModelDocumentHolder
   {
      private Document doc;

      public Document getDoc()
      {
         return doc;
      }

      public void setDoc( Document doc )
      {
         this.doc = doc;
      }

   }

答案 3 :(得分:0)

将自己的引用(A)传递给另一个对象(B)往往会在GUI代码中频繁发生。您可以使用上下文对象,该对象将传递给所有类,并且包含对相关引用的引用,并且可能包含一些全局信息。在简单的情况下,"主程序类"用作上下文并传播。

根据您使用的类别,这可能也很有用:Component#getParent()