一位经验丰富的开发人员告诉我,传递一个GUI实例是个坏主意。
基本上我有一个构建和显示GUI的类。在actionListener中,我创建了一个执行一些时间密集型任务的对象,并且我希望在完成任务的某些里程碑时显示状态。
以下是该类的简化版本:
public class MyGui extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel mainPanel;
private JPanel selectionPanel;
private JPanel activityPanel;
private JPanel executePanel;
private JButton connectButton;
private JButton disconnectButton;
private JButton abortButton;
private JList aList;
private JComboBox comboBox;
private JRadioButton primaryButton;
private JRadioButton secondaryButton;
private static JTextArea activityTextArea;
MyGui() {
this.setTitle("My Tool");
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(3, 1));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
mainPanel.setBackground(Color.darkGray);
this.add(mainPanel);
createMainSelectionArea();
createNodeSelectionArea();
createStatusArea();
createExecuteArea();
mainPanel.add(selectionPanel);
mainPanel.add(activityPanel);
mainPanel.add(executePanel);
this.add(mainPanel);
this.setResizable(false);
addActivity("test1");
addActivity("test2");
addActivity("test3");
addActivity("test4");
addActivity("test5");
addActivity("test6");
this.setSize(600, 400);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createMainSelectionArea() {
RadioButtonListener radioButtonListener = new RadioButtonListener();
primaryButton = new JRadioButton("Primary");
primaryButton.setBackground(Color.darkGray);
primaryButton.setForeground(Color.white);
primaryButton.addActionListener(radioButtonListener);
secondaryButton = new JRadioButton("Secondary");
secondaryButton.setBackground(Color.darkGray);
secondaryButton.setForeground(Color.white);
secondaryButton.addActionListener(radioButtonListener);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(primaryButton);
buttonGroup.add(secondaryButton);
JPanel buttonGroupPanel = new JPanel(new GridLayout(3, 1));
buttonGroupPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
buttonGroupPanel.setOpaque(true);
buttonGroupPanel.setBackground(Color.darkGray);
buttonGroupPanel.setForeground(Color.white);
buttonGroupPanel.add(primaryButton);
buttonGroupPanel.add(secondaryButton);
selectionPanel = new JPanel(new GridLayout(1, 2));
selectionPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
selectionPanel.setBackground(Color.darkGray);
selectionPanel.add(buttonGroupPanel);
}
private void createNodeSelectionArea() {
String[] data1 = {"one", "two", "three", "4", "5", "6"};
String[] data2 = {"four", "five", "six", "seven", "eight"};
ComboBoxListener comboBoxListener = new ComboBoxListener();
comboBox = new JComboBox(data1);
comboBox.setBorder(BorderFactory.createLineBorder(Color.white));
comboBox.setPreferredSize(new Dimension(150, 20));;
comboBox.setBackground(Color.white);
comboBox.setForeground(Color.black);
comboBox.addActionListener(comboBoxListener);
JPanel comboBoxPanel = new JPanel();
comboBoxPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
comboBoxPanel.setBackground(Color.darkGray);
comboBoxPanel.add(comboBox, BorderLayout.CENTER);
ListBoxListener listBoxListener = new ListBoxListener();
aList = new JList(data2);
aList.setBackground(Color.black);
aList.setForeground(Color.white);
aList.addListSelectionListener(listBoxListener);
JScrollPane scrollPane = new JScrollPane(aList);
scrollPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
scrollPane.setBackground(Color.darkGray);
JPanel listBoxPanel = new JPanel(new GridLayout(1,1));
TitledBorder border = BorderFactory.createTitledBorder("A Selection");
border.setTitleColor(Color.white);
border.setBorder(BorderFactory.createLineBorder(Color.white));
listBoxPanel.setBorder(border);
listBoxPanel.setBackground(Color.darkGray);
listBoxPanel.setForeground(Color.white);
listBoxPanel.add(scrollPane);
selectionPanel.add(comboBoxPanel);
selectionPanel.add(listBoxPanel);
}
private void createStatusArea() {
activityTextArea = new JTextArea();
activityTextArea.setEditable(false);
activityTextArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
activityTextArea.setBackground(Color.black);
activityTextArea.setForeground(Color.white);
JScrollPane scrollPane = new JScrollPane(activityTextArea);
scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scrollPane.setBackground(Color.darkGray);
TitledBorder activityTitle = BorderFactory.createTitledBorder("Status");
activityTitle.setTitleColor(Color.white);
activityTitle.setBorder(BorderFactory.createLineBorder(Color.white));
activityTitle.setTitlePosition(TitledBorder.CENTER);
activityPanel = new JPanel(new GridLayout(1, 1));
activityPanel.setBackground(Color.darkGray);
activityPanel.setBorder(activityTitle);
activityPanel.add(scrollPane);
}
public void addActivity(String activity) {
activityTextArea.append(activity + "\n");
activityTextArea.setCaretPosition(activityTextArea.getDocument().getLength());
}
public void createExecuteArea() {
ButtonListener buttonListener = new ButtonListener();
connectButton = new JButton("Connect");
connectButton.setPreferredSize(new Dimension(115, 30));
connectButton.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLineBorder(Color.lightGray)));
connectButton.setBackground(Color.white);
connectButton.addActionListener(buttonListener);
disconnectButton = new JButton("Disconnect");
disconnectButton.setPreferredSize(new Dimension(115, 30));
disconnectButton.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLineBorder(Color.lightGray)));
disconnectButton.setBackground(Color.white);
disconnectButton.addActionListener(buttonListener);
abortButton = new JButton("Abort");
abortButton.setPreferredSize(new Dimension(115, 30));
abortButton.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLineBorder(Color.lightGray)));
abortButton.setBackground(Color.white);
abortButton.addActionListener(buttonListener);
executePanel = new JPanel(new GridBagLayout());
executePanel.setBackground(Color.darkGray);
GridBagConstraints c = new GridBagConstraints();
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(Color.darkGray);
buttonPanel.add(connectButton);
buttonPanel.add(disconnectButton);
buttonPanel.add(abortButton);
executePanel.add(buttonPanel, c);
}
private class ButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(connectButton)) {
System.out.println("Connect Button");
}
if(e.getSource().equals(disconnectButton)) {
System.out.println("Disconnect Button");
}
if(e.getSource().equals(abortButton)) {
System.out.println("Abort Button");
}
}
}
private class RadioButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(primaryButton)) {
System.out.println("Primary Selected");
}
if(e.getSource().equals(secondaryButton)) {
System.out.println("Secondary Selected");
}
}
}
private class ComboBoxListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(comboBox)) {
System.out.println(comboBox.getSelectedItem());
DataClass dataClass = new DataClass(MyGui.this, otherStuff);
dataClass.doStuff(); // This class was calling the addActivity() method.
}
}
}
private class ListBoxListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
if(e.getSource().equals(aList)) {
System.out.println(aList.getSelectedValue());
}
}
}
}
所以我传递(假设该类名为MyGui)MyGui.this
到DataClass
,然后使用addActivity("status update");
所以我没有做以上操作,而是创建了这个界面:
public interface GuiUpdater {
void update(MyGui MyGui, String update);
}
并修改上面的MyGui
类来实现接口并以这种方式调用它:
@Override
public void update(MyGui myGui, String update) {
// TODO Auto-generated method stub
myGui.addActivity(update);
}
所以我可以从其他类中更新它,我也实现了这个接口。所以我将MyGui的实例从main传递给了其他类。
我认为这与我以前做过的事情完全相同(不同)。
这是使用接口的正确方法,如果不是,在不传递GUI实例的情况下从不同类更新GUI的正确方法是什么?
答案 0 :(得分:2)
接口用于定义对象可以支持的方法,而不需要接口的使用者了解对象或者希望如何实现接口。在您的情况下,您希望在MyGui
上提供更新方法,该方法不需要MyGui
类与您的其他代码之间的紧密耦合。如果您这样定义了接口,那么您可以编写可能需要更新GUI的所有代码,以便只与GuiUpdater
类型的对象进行交互:
public interface GuiUpdater {
void update(String update);
}
然后,您将修改MyGui
类声明以实现此接口:
public class MyGui extends JFrame implements GuiUpdater {
您还必须在MyGui
:
@Override
public void update(String update) {
this.addActivity(update);
}
这与您在问题中的内容类似,但有一个重要区别。因为它是作为MyGui
内的方法实现的,所以您可以访问MyGui
实例的所有内部状态(例如this
)。换句话说,您不需要将MyGui
实例作为参数传递,因为此方法位于MyGui
内。
现在,我们可以假设一个名为MyGui
的{{1}}实例具有可能需要更新GUI的功能,例如:
myGui
您可以像public void foo(GuiUpdater updater) {
updater.update("Interfaces are great");
}
一样调用此函数,因为foo(myGui)
满足该接口。这将MyGui
的实现与foo
的实现分离,意味着任何一方都不会对另一方的实现进行更改。
无论您如何构建软件(MVC,MVP等),编码到接口都是一个很好的习惯。它隐藏了实施细节,因此减少了更改这些细节的影响。