java对象间通信

标签 java swing oop design-patterns

还在学习Java。

Swing 又让我问了这个问题,但这确实是一个一般性的面向对象问题。如果我有一个主类(包含 main()),它会创建一个执行某些操作的新对象“A”,主类现在具有对该对象的引用,对象“B”如何访问该对象的属性?

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

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

一些代码(忽略细节):

主类,包含程序逻辑以及保存和加载方法等:

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
         }
     }
}

第一个问题是菜单项上的 Action 监听器调用一个方法来创建对象,但它不是对静态方法的调用。 第二个问题是它需要能够稍后由于另一个菜单选择而调用该新对象中的方法。

最佳答案

Model-View-Controller 中执行此操作的经典方法就是在运行时将对象绑定(bind)在一起。 Controller (您的操作监听器)采用参数来指示要操作的 View 和模型。

正如 Aqua 提到的,这种参数的使用也称为“依赖注入(inject)”。

   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"...
  }
}

要设置新文档,您可以创建一个“文档持有者”类。 “新建”菜单项将新文档放入 Holder 类中。所有其他菜单项从持有者类“获取”文档。这是一个相当严格的 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;
      }

   }

关于java对象间通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25099124/

相关文章:

java - 类似 ArrayList 声明之间的类型差异

java - 使用 java.bean API 检查非公共(public)类的实例

java - 计算器文本显示

java - 这个java设计模式的名字是什么?

java - 动态规划-记忆化

java - java 方法如何接受扩展泛型类型的参数

java - JPanel 出现在两个位置

c++ - 在调用函数时使用相同的对象实例作为参数和调用对象

c++ - 将对未初始化对象的引用传递给父类(super class)构造函数,然后用它的移动构造函数初始化所述对象?