如何摆脱函数中的重复代码?

How to get rid of repeating code in function?

我想从应用程序中删除重复代码。我尝试了多种方法,但当我这样做时,应用程序并没有像我预期的那样工作。只有将相同的代码放在一个函数中才会有效果。

简而言之,在应用程序中,我可以通过两种方式删除记录,一种是按下按钮,另一种是指向一条记录并用鼠标右键将其删除。删除按钮可以,但是不知道如何让鼠标删除也有同样的效果。

删除 table 中记录的按钮。

 deleteButton.addActionListener(event -> {
    
                String name;
                name = Name.getText();
    
                try {
                    removeSelectedRow(table1);
    
                    pst = con.prepareStatement("delete from recipe where recipe_name = ?");
                    pst.setString(1, name);
                    pst.executeUpdate();
                    JOptionPane.showMessageDialog(null, "Record deleted");
                    Name.setText("");
                    Time.setText("");
                    Difficulty.setSelectedItem("");
                    Name.requestFocus();
    
                } catch (SQLException e) {
    
                    e.printStackTrace();
                }
    
    
            });

这里有一个应该用鼠标右键删除的函数,如您所见,它可以工作,但代码与前面的示例几乎相同。

 public void setDeleteButton(ActionEvent event) {


        JMenuItem menu = (JMenuItem) event.getSource();
        if (menu == menuItemRemove) {
            removeSelectedRow(table1);

        }
        String name;
        name = Name.getText();

        try {
            removeSelectedRow(table1);

            pst = con.prepareStatement("delete from recipe where recipe_name = ?");
            pst.setString(1, name);
            pst.executeUpdate();
            JOptionPane.showMessageDialog(null, "Record deleted");
            Name.setText("");
            Time.setText("");
            Difficulty.setSelectedItem("");
            Name.requestFocus();

        } catch (SQLException e) {

            e.printStackTrace();
        }
    }

指向特定记录的函数

 public void removeSelectedRow(JTable table) {

        DefaultTableModel model = (DefaultTableModel) table1.getModel();
        if (table.getSelectedRow() != -1) {
            model.removeRow(table.getSelectedRow());

        }
    }

好的,所以这需要稍微改变一下心态。要使其真正灵活,您将需要支持“依赖注入”和“委派”等概念。

原因是,您的“操作”需要大量信息,但是,我们应该朝着 类 之间低水平的内聚或耦合方向发展。例如,您的“操作”不应该关心“如何”删除该行,只关心在请求时应该完成。

那么,让我们从一些基本的委派开始...

public interface MutableTableSupportable {
    public void addListSelectionListener(ListSelectionListener listener);
    public void removeListSelectionListener(ListSelectionListener listener);
}

public interface TableRowDeletable extends MutableTableSupportable {
    public int getSelectedRowCount();
    public void removeSelectedRow();
}

现在,很明显,我过度简化了这一点以满足我的更多需求,但在这里我提供了一个“基本”级别的委托和更有针对性的委托。为什么?因为如果您想提供“插入”操作会怎样?为什么要有“删除”功能?相反,我们故意隔离我们想要公开的功能。

接下来,我们需要设计我们的动作...

public class DeleteRowAction extends AbstractAction {

    private TableRowDeletable delgate;

    public DeleteRowAction(TableRowDeletable delgate) {
        putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
        putValue(MNEMONIC_KEY, KeyEvent.VK_D);
        putValue(NAME, "Delete Row");
        putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
        this.delgate = delgate;

        delgate.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                selectionDidChange();
            }
        });
        selectionDidChange();
    }

    protected void selectionDidChange() {
        setEnabled(delgate.getSelectedRowCount() > 0);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        delgate.removeSelectedRow();
    }

}

好的,没什么特别的,这就是重点。它监视选择状态,因此我们可以 enable/disable 操作,当触发时,我们调用我们的委托来完成实际工作。这将操作与实现分离,因为操作不需要知道正在使用什么类型的 TableModel 或者它可能正在使用什么类型的数据源,它只是想告诉委托它应该携带进行某种操作。

另请注意,我们设置了一个键盘快捷键,可用于 JMenuItem 和助记符支持(按住 Alt选项键)

好的,但这对我们来说真的很重要,或者是...

让我们看看删除操作可能是什么样子...

DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
    @Override
    public int getSelectedRowCount() {
        return table.getSelectedRowCount();
    }

    @Override
    public void removeSelectedRow() {
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        int visibleRowIndex = table.getSelectedRow();
        if (visibleRowIndex == -1) {
            return;
        }

        int modelIndex = table.convertRowIndexToModel(visibleRowIndex);
        // I'm guessing here, but if you're deleting a row, you should
        // use the row data
        String recordId = (String) model.getValueAt(modelIndex, 0);

        try (PreparedStatement pst = getConnection().prepareStatement("delete from recipe where recipe_name = ?")) {
            pst.setString(1, recordId);
            // You could check the number of rows effected by this change
            pst.executeUpdate();

            JOptionPane.showMessageDialog(TestPane.this, "Record deleted", "Success", JOptionPane.INFORMATION_MESSAGE);

            model.removeRow(modelIndex);
        } catch (SQLException ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(TestPane.this, "Failed to delete row from database", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    @Override
    public void addListSelectionListener(ListSelectionListener listener) {
        table.getSelectionModel().addListSelectionListener(listener);
    }

    @Override
    public void removeListSelectionListener(ListSelectionListener listener) {
        table.getSelectionModel().removeListSelectionListener(listener);
    }
});

现在,这只是一个示例,但基本思想是,我们已经为 MutableTableSupportableTableRowDeletable 接口提供了实现(但 DeleteRowAction 没有关心“如何”),我们已经实现了 removeSelectedRow 功能来从 TableModel 和数据库中删除行。

同样,DeleteRowAction 不关心这是如何实现的,它只是委托责任,所以你可以有多个 DeleteRowActions,它们与不同的 TableModels 和数据一起工作同时来源

代表团

好的,但是所有这些如何协同工作?嗯,其实,真的很容易,事实上

可运行示例...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.sql.*;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JMenuBar menuBar = new JMenuBar();
                JFrame frame = new JFrame();
                frame.setJMenuBar(menuBar);
                frame.add(new TestPane(menuBar));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTable table;

        public TestPane(JMenuBar menuBar) {
            setLayout(new BorderLayout());

            DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
            table = new JTable(model);

            add(new JScrollPane(table));

            DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
                @Override
                public int getSelectedRowCount() {
                    return table.getSelectedRowCount();
                }

                @Override
                public void removeSelectedRow() {
                    JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
                }

                @Override
                public void addListSelectionListener(ListSelectionListener listener) {
                    table.getSelectionModel().addListSelectionListener(listener);
                }

                @Override
                public void removeListSelectionListener(ListSelectionListener listener) {
                    table.getSelectionModel().removeListSelectionListener(listener);
                }
            });

            JMenu actionsMenu = new JMenu("Actions");
            actionsMenu.add(deleteRowAction);

            menuBar.add(actionsMenu);

            JButton deleteButton = new JButton(deleteRowAction);
            add(deleteButton, BorderLayout.SOUTH);
        }

    }

    public interface MutableTableSupportable {
        public void addListSelectionListener(ListSelectionListener listener);
        public void removeListSelectionListener(ListSelectionListener listener);
    }

    public interface TableRowDeletable extends MutableTableSupportable {
        public int getSelectedRowCount();
        public void removeSelectedRow();
    }

    public class DeleteRowAction extends AbstractAction {

        private TableRowDeletable delgate;

        public DeleteRowAction(TableRowDeletable delgate) {
            putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
            putValue(MNEMONIC_KEY, KeyEvent.VK_D);
            putValue(NAME, "Delete Row");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
            this.delgate = delgate;

            delgate.addListSelectionListener(new ListSelectionListener() {
                @Override
                public void valueChanged(ListSelectionEvent e) {
                    selectionDidChange();
                }
            });
            selectionDidChange();
        }

        protected void selectionDidChange() {
            setEnabled(delgate.getSelectedRowCount() > 0);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            delgate.removeSelectedRow();
        }

    }
}

nb:此示例删除了数据库支持,因为我没有,而是显示一条消息

好的,让我们快速浏览一下这里的一些有趣的东西...

首先...

JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));

我们将菜单栏注入到面板中。这样做是为了让面板可以根据需要配置菜单栏。我们可以在这里使用一种类型的工厂或其他委托,但我会把它留给你自己想办法。

下一个...

JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);

menuBar.add(actionsMenu);

JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);

我们构建 JMenu 并添加我们的删除行操作并创建一个 JButton,使用相同的 Action ... 五行代码,我们实际上已经完成很多。我们已经能够设置每个组件显示的文本、工具提示文本、加速键和助记符...尝试手动执行此操作,然后需要更改一些东西(想要支持本地化 - 需要制作一个位置的变化)

但是等等,我们可以做得更多!!

如果我们添加...

InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();

inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);

到构造函数的末尾,我们可以为用户提供一个键绑定,这样当JTable有键盘焦点并且命中他们Delete /Backspace键,也会触发动作!!!

现在我们有四种触发动作的方法:

  1. 按下按钮
  2. 打开并触发菜单项
  3. 使用菜单键盘“加速器”键绑定
  4. 点击删除

操作

键绑定可运行示例...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JMenuBar menuBar = new JMenuBar();
                JFrame frame = new JFrame();
                frame.setJMenuBar(menuBar);
                frame.add(new TestPane(menuBar));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTable table;

        public TestPane(JMenuBar menuBar) {
            setLayout(new BorderLayout());

            DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
            table = new JTable(model);

            add(new JScrollPane(table));

            DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
                @Override
                public int getSelectedRowCount() {
                    return table.getSelectedRowCount();
                }

                @Override
                public void removeSelectedRow() {
                    JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
                }

                @Override
                public void addListSelectionListener(ListSelectionListener listener) {
                    table.getSelectionModel().addListSelectionListener(listener);
                }

                @Override
                public void removeListSelectionListener(ListSelectionListener listener) {
                    table.getSelectionModel().removeListSelectionListener(listener);
                }
            });

            JMenu actionsMenu = new JMenu("Actions");
            actionsMenu.add(deleteRowAction);

            menuBar.add(actionsMenu);

            JButton deleteButton = new JButton(deleteRowAction);
            add(deleteButton, BorderLayout.SOUTH);

            InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
            ActionMap actionMap = table.getActionMap();

            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
            actionMap.put("deleteRow", deleteRowAction);
        }

    }

    public interface MutableTableSupportable {
        public void addListSelectionListener(ListSelectionListener listener);
        public void removeListSelectionListener(ListSelectionListener listener);
    }

    public interface TableRowDeletable extends MutableTableSupportable {
        public int getSelectedRowCount();
        public void removeSelectedRow();
    }

    public class DeleteRowAction extends AbstractAction {

        private TableRowDeletable delgate;

        public DeleteRowAction(TableRowDeletable delgate) {
            putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
            putValue(MNEMONIC_KEY, KeyEvent.VK_D);
            putValue(NAME, "Delete Row");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
            this.delgate = delgate;

            delgate.addListSelectionListener(new ListSelectionListener() {
                @Override
                public void valueChanged(ListSelectionEvent e) {
                    selectionDidChange();
                }
            });
            selectionDidChange();
        }

        protected void selectionDidChange() {
            setEnabled(delgate.getSelectedRowCount() > 0);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            delgate.removeSelectedRow();
        }

    }
}

但这还不是全部!我们还可以向 JToolBar 添加一个按钮,因为,为什么不呢!?