Refactoring a Spring + Swing (MVC) Application from developerWorks / IBM..

.. and removing the Spring framework, and fixing just a very tiny bug in the way..

So somehow I found myself on this page and decided to play with it. You can download the original code found on IBM 's page from here. This is what I ended up with myself.

Directory Layout


TodoList.java
package todo.model;
 
import java.util.ArrayList;
 
public class TodoList extends ArrayList<String> {
 
    // Comes with sample data..
    {
        add("Item 1");
        add("Item 2");
        add("Item 3");
    }
}

ItemTableModel.java
package todo.view.tables;
 
import todo.model.TodoList;
 
import javax.swing.table.AbstractTableModel;
 
public class ItemTableModel extends AbstractTableModel {
 
    private TodoList todoList;
 
    public boolean isCellEditable(final int rowIndex, final int columnIndex) {
        return true;
    }
 
    public int getColumnCount() {
        return 1;
    }
 
    public String getColumnName(final int column) {
        return "Items";
    }
 
    public void setTodoList(final TodoList todoList) {
        this.todoList = todoList;
    }
 
    public int getRowCount() {
        return todoList.size();
    }
 
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
        todoList.set(rowIndex, (String) value);
    }
 
    public Object getValueAt(int rowIndex, int columnIndex) {
        return todoList.get(rowIndex);
    }
}

AddNewButton.java
package todo.view.buttons;
 
import javax.swing.*;
 
public class AddNewButton extends JButton {
 
    public AddNewButton() {
        super("Add New");
    }
}

DeleteButton.java
package todo.view.buttons;
 
import javax.swing.*;
 
public class DeleteButton extends JButton {
 
    public DeleteButton() {
        super("Delete");
    }
}

ButtonsPanel.java
package todo.view.panels;
 
import todo.view.buttons.AddNewButton;
import todo.view.buttons.DeleteButton;
 
import javax.swing.*;
 
public class ButtonsPanel extends JPanel {
 
    public ButtonsPanel(final AddNewButton addNewButton, final DeleteButton deleteButton) {
        final BoxLayout boxLayout = new BoxLayout(this, BoxLayout.X_AXIS);
        setLayout(boxLayout);
 
        add(addNewButton);
        add(deleteButton);
    }
}

MainPanel.java
package todo.view.panels;
 
import javax.swing.*;
 
public class MainPanel extends JPanel {
    public MainPanel(final JScrollPane itemScrollPane, final ButtonsPanel buttonPanel) {
        final BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
        setLayout(boxLayout);
 
        add(itemScrollPane);
        add(buttonPanel);
    }
}


AddNewButtonActionListener.java
package todo.controller;
 
import todo.model.TodoList;
 
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class AddNewButtonActionListener implements ActionListener {
 
    private TodoList todoList;
    private JTable table;
 
    public void setTodoList(final TodoList todoList) {
        this.todoList = todoList;
    }
 
    public void setTable(final JTable itemTable) {
        this.table = itemTable;
    }
 
    public void actionPerformed(ActionEvent e) {
        todoList.add("New Item");
        table.revalidate();
    }
}

DeleteButtonActionListener.java
package todo.controller;
 
import todo.model.TodoList;
 
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class DeleteButtonActionListener implements ActionListener {
 
    private TodoList todoList;
    private JTable table;
 
    public void setTodoList(final TodoList todoList) {
        this.todoList = todoList;
    }
 
    public void setTable(final JTable itemTable) {
        this.table = itemTable;
    }
 
    public void actionPerformed(final ActionEvent e) {
        final int selectedRow = table.getSelectedRow();
 
        // if there is no selected row, don't do anything
        if (selectedRow == -1) {
            return;
        }
 
        // if we are editing the table, don't do anything
        if (table.isEditing()) {
            return;
        }
 
        todoList.remove(selectedRow);
 
        table.clearSelection();
        table.revalidate();
    }
}

MainFrame.java
package todo.view;
 
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
 
public class MainFrame extends JFrame {
    public void init() {
        setTitle("My Todo List");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(new Dimension(600, 400));
        setVisible(true);
    }
}

App.java
package todo;
 
import todo.controller.AddNewButtonActionListener;
import todo.controller.DeleteButtonActionListener;
import todo.model.TodoList;
import todo.view.MainFrame;
import todo.view.buttons.AddNewButton;
import todo.view.buttons.DeleteButton;
import todo.view.panels.ButtonsPanel;
import todo.view.panels.MainPanel;
import todo.view.tables.ItemTableModel;
 
import javax.swing.*;
 
public class App {
    public static void main(String[] args) {
        // Model
        final TodoList itemList = new TodoList();
 
        // View
        final ItemTableModel itemTableModel = new ItemTableModel();
        final JTable itemTable = new JTable();
        itemTable.setModel(itemTableModel);
        final JScrollPane scrollableTable = new JScrollPane(itemTable);
 
        final AddNewButton addNewButton = new AddNewButton();
        final DeleteButton deleteButton = new DeleteButton();
 
        final ButtonsPanel buttonPanel = new ButtonsPanel(addNewButton, deleteButton);
        final MainPanel mainPanel = new MainPanel(scrollableTable, buttonPanel);
 
        // Controller
        final AddNewButtonActionListener addNewButtonActionListener = new AddNewButtonActionListener();
        final DeleteButtonActionListener deleteButtonActionListener = new DeleteButtonActionListener();
 
        // Bind Model to Views..
        itemTableModel.setTodoList(itemList);
 
        // Bind Controllers to Views..
        addNewButton.addActionListener(addNewButtonActionListener);
        deleteButton.addActionListener(deleteButtonActionListener);
 
        // Bind Model to Controllers..
        addNewButtonActionListener.setTodoList(itemList);
        deleteButtonActionListener.setTodoList(itemList);
 
        // Bind Views to Controllers..
        addNewButtonActionListener.setTable(itemTable);
        deleteButtonActionListener.setTable(itemTable);
 
        // Pack everything in the Main Frame and make it visible..
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final MainFrame mainFrame = new MainFrame();
                mainFrame.setContentPane(mainPanel);
                mainFrame.init();
            }
        });
    }
}

Notes &  Discussion

So the Model - View - Controller implementation that is used here is nothing like the ones I have studied!

Compared to the first example, here the Controllers are not invoking methods on the View, but rather on the model directly. In the first example, Controllers were only invoking methods on Views.

Compared to the second example, the model here is not actually notifying the related Views, where as this was the case in my example. Here, the Controller seems to modify the model directly and then force the View to update itself. The View gets the data directly from the model, data is not passed from the Controller to the View..

This example actually resembles something I have done some time ago, here. For example, see how the Controller there first modifies (or reads) the model, and then passes data to be rendered to the View.
 public void listAllActors() {
    final List<Actor> actors = actorService.allActors();
    actorView.print(actors);
}
This example also resembles what I have tried here, somewhat. But this is getting way confusing for me and I have no idea which approach is the correct Model - View - Controller implementation and / or is the one to be used!