SCJD: My MVC Architecture

So MVC (Model-View-Controller) seemed fairly straightforward as framework for the assignment. I think my understanding of it was pretty much text book.

Role of each component

Model – Abstracts how information is stored in the back end system.
View – Solely responsible for rendering the GUI.
Controller – Maps user actions on the view to business processes on the model.

How they work together

Checkout this link for the classic MVC architecture diagram.

http://en.wikipedia.org/wiki/Image:ModelViewControllerDiagram.png

The solid lines in the diagram indicate direct knowledge of another component. The dotted lines indicate indirect knowledge of another component. Let’s briefly discuss each component.

The Controller

In the diagram you can see that the Controller has direct knowledge of the view and the model. In my assignment, the controller had a reference to view in order to delegate to the view to access user entered values (ex. a customer id or search criteria) and to register the view as a model listener (more on that later).

The controller also has direct knowledge of the model. This is necessary so that it can delegate to the model to do things as a response to a user action; such as search the database or persist something to the database.

The View

The view has direct knowledge of the model. The view has a JTable of data which it gets from the model of course. This is where the Observer Pattern comes in. The view is registered as a listener of the model (an Observer), that way when the model changes, the view is notified with updated data and can redraw itself with the new data. (again, more on this to come).

You will notice that the diagram indicates that the view has indirect knowledge of the controller. This is necessary so that the controller can listen to user events on the view (ex. a user button click). I actually implemented my solution a little differently. i.e. In my MVC diagram, the view has NO indirect knowledge of the controller.

So how did I do this? Simple, I wrote access methods (or “Hook” methods). For the components that the controller will want to listen to, I just wrote public methods that accepted an ActionListener and
set them on the component(s). This way, the view has no knowledge that the controller is specifically listening for actions of its components. It knows somebody’s listening, but just not who exactly. I guess that could count as an indirect reference to the controller, but I didn’t think so.

The Model

The model doesn’t have direct knowledge of any of the other components in MVC. As the object being observed (the Observable), it does however have indirect knowledge of its Observers. Each listener of the model must be register as a model listener and the model maintains a collection of listeners. Thus, when the model changes, it just iterates over that collection and notifies each listener (i.e. views) of the change and then they can redraw themselves with the new data.

Let’s look at some code

So let’s put this all together. The application is a simple application that allows a CSR to book a hotel room for a particular customer. The CSR has a “customer id” that must be persisted to the record of an available room.

So I have some components on the View which the Controller wants to listen to and thus know when they are clicked. The view exposes the action listener for the components (via a hook method).

public void addBookActionListener(ActionListener listener) {
    this.bookButton.addActionListener(listener);
    this.bookToolbarButton.addActionListener(listener);
    this.bookMenuItem.addActionListener(listener);
}

Now the Controller can provide a single ActionListener for booking components. Let’s see how the Controller registers as a listener to these components.

private void addBookActionListener() {
    ActionListener bookListener = new ActionListener() {
        public void actionPerformed(ActionEvent event) {
            BookingRecord bookingRecord = buildBookingRecord();

            this.log.info(BOOKING_CONTROLLER + "Attempting to book "
                  + "record # " + bookingRecord.getRecordNumber());

            this.bookingModel.bookRecord(bookingRecord);

            this.log.info(BOOKING_CONTROLLER + "Successfully booked "
                  + "record # " + bookingRecord.getRecordNumber());
        }
    };

    this.bookingView.addBookActionListener(bookListener);
}

So whenever an action is performed on any of the BookingView booking components, the actionPerformed() method is called and the controller delegates to the view to return the selected row of data, constructs a model object from the row data, then delegates to the model to update the record.

So we’ve seen what happens when the user hits a “book” button and exactly how the the data is sent to and persisted by the model. But once the model has been updated, how does the view get updated?

Enter the BookingModelListener

This is an interface for Observers of the model to implement.

public interface BookingModelListener {
    ...
    public static final int MODEL_CHANGED = 1;
    ...
    public void modelChanged(int notificationType, BookingRecord[] records);
}

This defines a contract of what the model will do once its been updated. When the model is changed, Observers will be provided with a notification type and array of model objects, which will be converted and put into the view’s JTable table model.

View becomes a BookingModelListener

The view implements the interface and delegates to the table model to update its data and redraw the table when its notified by the model.

public void modelChanged(int notificationType, BookingRecord[] records) {
...
    if (BookingModelListener.MODEL_CHANGED == notificationType) {
        this.bookingTableModel.setTableModel(records);
        this.bookingTableModel.fireTableDataChanged();
    	...
    }
}

The Model notifies its listeners

So when a service request is made on the model and data has changed, the model has to notify its listeners. The model doesn’t know directly who its listeners are, but it knows they are BookingModelListener’s and they must be notified. The call to this method will be in a model business method like bookRecord()…

private void fireModelChanged(int notificationType, BookingRecord[] records) {
        for (int i = 0; i < this.listeners.size(); i++) {
            BookingModelListener listener = ((ArrayList<BookingModelListener>) this.listeners)
                    .get(i);
            listener.modelChanged(notificationType, records);
        }
    }

And voila! That’s it. From when the user clicks the book button, the controller tells the model to update itself. Once updated, the model notifies its listeners and the views are updated automagically (I love that word, automagically, I wish I had came up with that).

Questions

  • How would you implement the indirect relationship of the view and the controller? I used “hook” methods, but I’m sure there are better ways to do this; i.e. without exposing action listeners.
  • One thing I noticed once I took a step back was just how much the controller knew about. It seems very powerful, because it knows and does quite a lot. Is this inherent to the pattern? Or are there changes that can be made to reduce its knowledge?

Next Post

The next post will be advantages and disadvantages I’ve seen from using MVC.

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply