Home > Software engineering >  How to get rid of repeating code in function?
How to get rid of repeating code in function?

Time:01-12

I want to get rid of the repeating code from an application. I tried to do it in several ways, but when I did it, the application did not work as I expected. Only putting the same code in a function had an effect.

In a nutshell, in the application I can delete records in 2 ways, either by pressing a button or by pointing to a record and deleting it with the right mouse button. Button deletion works, but I do not know how to make the mouse deletion have the same effect.

Button that deletes a record in a 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();
                }
    
    
            });

here is a function that is supposed to delete with the right mouse button, as you can see it works but the code is almost identical to the previous example.

 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();
        }
    }

function that points to a specific record

 public void removeSelectedRow(JTable table) {

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

        }
    }

CodePudding user response:

Okay, so this is going to require a slight shift in mindset. To make this truely flexible, you're going to need to support concepts such as "dependency inject" and "delegation".

The reason for this is, you "operation" needs a lot of information, but, we should be driving towards having a low level of cohesion or coupling between our classes. Your "operation", for example, shouldn't care "how" the row is deleted, only that when requested, it should be done.

So, lets start with some basic delegation...

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

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

Now, obviously, I'm overly simplifying this for more my needs, but here I've provided a "basic" level delegate and more focused delegate. Why? Because what happens if you want to provide a "insert" action? Why should it have "delete" functionality? Instead, we deliberately isolate the functionality we want to expose.

Next, we need to design our action...

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();
    }

}

Okay, nothing really special, which is kind of the point. It monitors the selection state so we can enable/disable the action and when triggered, we call our delegate to do the actual work. This decouples the action from the implementation, as the action doesn't need to know what type of TableModel is in use or what type of data source it might be using, it just wants to tell the delegate that it should carry out some kind of operation.

Also note, we've set up a keyboard short cut, which can be used by the JMenuItem and mnemonic support (hold down the Alt or Option key)

Okay, but that's really doing a lot for us, or is it...

Let's have a look at what a delete action might look like...

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

Now, this is just an example, but the basic idea is, we've provide implementation for both the MutableTableSupportable and TableRowDeletable interfaces (but the DeleteRowAction doesn't care about the "how") and we've implemented the removeSelectedRow functionality to delete the row from the TableModel and database.

Again, DeleteRowAction doesn't care how this is implemented, it's just delegating that responsibility, so you could have multiple DeleteRowActions which work with different TableModels and data sources all at the same time

  •  Tags:  
  • Related