通常需要根据另一个GUI对象的状态更改其他GUI对象的行为.例如.按下按钮时,标签应更改其名称.但是,当我使用像JButton这样的AbstractAction对象时myButton = new JButton(myButtonAction);我需要对继承自AbstractAction的对象中的GUI对象的引用.我应该只是在GUI中创建AbstractAction对象,然后将所有必要的GUI引用传递给AbstractAction对象,还是可以认为是坏样式?
为了使它更具体:
// AbstractAction public class MyAction extends AbstractAction { public MyAction(String name,String description,Integer mnemonic,JLabel) { super(name); putValue(SHORT_DESCRIPTION,description); putValue(MNEMONIC_KEY,mnemonic); } public void actionPerformed(ActionEvent e) { // do something } } } public class GUI{ public Action myAction = null; public GUI(){ JLabel label = new JLabel("text"); //This is not a good idea: myAction = new MyAction("some text",desc,new Integer(KeyEvent.VK_Q),label); JButton myButton = new JButton(myAction); } }
解决方法
你想尽可能地松开耦合,而不是像你的问题所暗示的那样收紧耦合,为了做到这一点,我认为你应该进一步抽象,通过将部分进一步分离成一个成熟的MVC程序.然后,侦听器(Action)可以更改模型,作为GUI的视图可以监听模型的更改并进行相应的响应.
例如:
import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.event.SwingPropertyChangeSupport; public class MvcEg { private static void createAndShowGui() { View view = new MvcEgView(); Model model = new MvcEgModel(); new MvcEgControl(model,view); JFrame frame = new JFrame("MvcEg"); frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(view.getMainPanel()); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } interface View { void setMyButtonAction(Action action); Component getMainPanel(); void setStatusLabelText(String text); } @SuppressWarnings("serial") class MvcEgView implements View { private static final int PREF_W = 500; private static final int PREF_H = 400; private static final String STATUS_TEXT = "Status: "; private JPanel mainPanel = new JPanel() { @Override public Dimension getPreferredSize() { return new Dimension(PREF_W,PREF_H); } }; private JLabel statusLabel = new JLabel(STATUS_TEXT,SwingConstants.CENTER); private JButton myButton = new JButton(); public MvcEgView() { JPanel btnPanel = new JPanel(new GridBagLayout()); btnPanel.add(myButton); mainPanel.setLayout(new BorderLayout()); mainPanel.add(btnPanel,BorderLayout.CENTER); mainPanel.add(statusLabel,BorderLayout.SOUTH); } @Override public void setMyButtonAction(Action action) { myButton.setAction(action); } @Override public void setStatusLabelText(String text) { statusLabel.setText(STATUS_TEXT + text); } @Override public Component getMainPanel() { return mainPanel; } } interface Model { public static final String MOD_FIVE_STATUS = "mod five status"; void incrementStatus(); ModFiveStatus getModFiveStatus(); void removePropertyChangeListener(PropertyChangeListener listener); void addPropertyChangeListener(PropertyChangeListener listener); void setModFiveStatus(ModFiveStatus modFiveStatus); } class MvcEgModel implements Model { private ModFiveStatus modFiveStatus = ModFiveStatus.ZERO; private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport( this); @Override public void incrementStatus() { int value = modFiveStatus.getValue(); value++; value %= ModFiveStatus.values().length; setModFiveStatus(ModFiveStatus.getValuesStatus(value)); } @Override public void setModFiveStatus(ModFiveStatus modFiveStatus) { ModFiveStatus oldValue = this.modFiveStatus; ModFiveStatus newValue = modFiveStatus; this.modFiveStatus = modFiveStatus; pcSupport.firePropertyChange(MOD_FIVE_STATUS,oldValue,newValue); } @Override public ModFiveStatus getModFiveStatus() { return modFiveStatus; } @Override public void addPropertyChangeListener(PropertyChangeListener listener) { pcSupport.addPropertyChangeListener(listener); } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { pcSupport.removePropertyChangeListener(listener); } } enum ModFiveStatus { ZERO(0,"Zero"),ONE(1,"One"),TWO(2,"Two"),THREE(3,"Three"),FOUR(4,"Four"); private int value; private String text; private ModFiveStatus(int value,String text) { this.value = value; this.text = text; } public int getValue() { return value; } public String getText() { return text; } public static ModFiveStatus getValuesStatus(int value) { if (value < 0 || value >= values().length) { throw new ArrayIndexOutOfBoundsException(value); } for (ModFiveStatus modFiveStatus : ModFiveStatus.values()) { if (modFiveStatus.getValue() == value) { return modFiveStatus; } } // default that should never happen return null; } } @SuppressWarnings("serial") class MvcEgControl { private Model model; private View view; public MvcEgControl(final Model model,final View view) { this.model = model; this.view = view; view.setMyButtonAction(new MyButtonAction("My Button",KeyEvent.VK_B)); view.setStatusLabelText(model.getModFiveStatus().getText()); System.out.println("model's status: " + model.getModFiveStatus()); System.out.println("model's status text: " + model.getModFiveStatus().getText()); model.addPropertyChangeListener(new ModelListener()); } private class MyButtonAction extends AbstractAction { public MyButtonAction(String text,int mnemonic) { super(text); putValue(MNEMONIC_KEY,mnemonic); } @Override public void actionPerformed(ActionEvent e) { model.incrementStatus(); System.out.println("button pressed"); } } private class ModelListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(Model.MOD_FIVE_STATUS)) { String status = model.getModFiveStatus().getText(); view.setStatusLabelText(status); System.out.println("status is: " + status); } } } }
在我看来,关键是模型对视图一无所知,并且视图对模型知之甚少(这里没有).