Trying to understand the Model - View - Controller Pattern..

.. without success!

So I have been reading Trygve Reenskaug 's paper on the Model - View - Controller pattern and took some notes.. But before diving into that, let 's start first with this answer from StackOverflow, which I like a lot!

Almost everyone agrees that generally:
  • Models implement business logic, and encapsulate data access.
  • Views implement presentation logic.
  • Controllers handle input and control flow.
How they connect and talk to one another is a matter of debate, argument, fistfights, and holy wars. In practice, controllers usually load views and models, views sometimes load models and controllers, but models don't generally load controllers or views.

Ok, moving on with the notes from Reenskaug 's paper..

Views

A view is a (visual) representation of its model. It would ordinarily highlight certain attributes of the model and suppress others.

A view is attached to its model (or model part) and gets the data necessary for the presentation from the model by asking questions. It may also update the model by sending appropriate messages. All these questions and messages have to be in the terminology of the model, the view will therefore have to know the semantics of the attributes of the model it represents.

A view should never know about user input, such as mouse operations and keystrokes. It should always be possible to write a method in a controller that sends messages to views which exactly reproduce any sequence of user commands.

Controllers

A Controller provides means for user input by presenting the user with menus or other means of giving commands and data. The controller receives such user input, translates it into the appropriate messages and pass these messages onto one or more of the views.

Questions in my Mind..
I wonder what is meant by controller translating user input into messages.

Swingy Notes

So I wanted to follow the specification given and implement a very basic application called "Swingy Notes" which is a useless note taking application that uses the Swing library..

This is what the application itself looks like:


Directory Layout

Model

SwingyNote.java
package biz.tugay.swingynotes.model;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class SwingyNote {
 
    private final String note;
 
    public SwingyNote(final String note) {
        this.note = note;
    }
 
    public String getNote() {
        return note;
    }
}

SwingyNoteService.java
package biz.tugay.swingynotes.model;
 
import java.util.*;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public final class SwingyNoteService {
 
    private final Set<SwingyNote> swingyNotes = new HashSet<SwingyNote>();
 
    // Add some sample data..
    {
        final SwingyNote sampleNote = new SwingyNote("Sample Note!");
        addNote(sampleNote);
 
        final SwingyNote helloWorld = new SwingyNote("Hello World!");
        addNote(helloWorld);
    }
 
    public void addNote(final SwingyNote swingyNote) {
        swingyNotes.add(swingyNote);
    }
 
    public Collection<SwingyNote> getAllNotes() {
        return swingyNotes;
    }
}

Notes
So the Model seems to know nothing about the Views and Controllers so I think all is fine here..

View

AllNotesPanel.java
package biz.tugay.swingynotes.view;
 
import biz.tugay.swingynotes.model.SwingyNote;
import biz.tugay.swingynotes.model.SwingyNoteService;
 
import javax.swing.*;
import java.awt.*;
import java.util.Collection;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AllNotesPanel extends JPanel {
 
    private final SwingyNoteService swingyNoteService;
 
    public AllNotesPanel(final SwingyNoteService swingyNoteService) {
        this.swingyNoteService = swingyNoteService;
        displayAllNotes();
    }
 
    public void displayAllNotes() {
        removeAll();
        final Collection<SwingyNote> allNotes = swingyNoteService.getAllNotes();
        for (final SwingyNote swingyNote : allNotes) {
            final JLabel swingyNoteLabel = buildLabelForSwingyNote(swingyNote);
            add(swingyNoteLabel);
        }
        validate();
        repaint();
    }
 
    private JLabel buildLabelForSwingyNote(final SwingyNote swingyNote) {
        final JLabel swingyNoteLabel = new JLabel(swingyNote.getNote());
        swingyNoteLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
        swingyNoteLabel.setFont(new Font("Dialog", Font.BOLD, 27));
        swingyNoteLabel.setOpaque(true);
        swingyNoteLabel.setBackground(Color.YELLOW);
        return swingyNoteLabel;
    }
}

AddNewNotePanel.java
package biz.tugay.swingynotes.view;
 
import biz.tugay.swingynotes.model.SwingyNote;
import biz.tugay.swingynotes.model.SwingyNoteService;
 
import javax.swing.*;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AddNewNotePanel extends JPanel {
 
    private final SwingyNoteService swingyNoteService;
    private final JTextField newNoteInputTextField;
 
    public AddNewNotePanel(final SwingyNoteService swingyNoteService) {
        this.swingyNoteService = swingyNoteService;
        this.newNoteInputTextField = new JTextField(20);
 
        add(newNoteInputTextField);
    }
 
    public void addNote() {
        final SwingyNote swingyNote = new SwingyNote(newNoteInputTextField.getText());
        swingyNoteService.addNote(swingyNote);
        newNoteInputTextField.setText(null);
    }
}

SwingyNotesMainFrame.java
package biz.tugay.swingynotes.view;
 
import javax.swing.*;
import java.awt.*;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class SwingyNotesMainFrame extends JFrame {
 
    public SwingyNotesMainFrame(final AllNotesPanel allNotesPanel, final AddNewNotePanel addNewNotePanel) {
        super("Swingy Notes");
 
        // Set Layout..
        final BorderLayout borderLayout = new BorderLayout();
        final Container contentPane = getContentPane();
        contentPane.setLayout(borderLayout);
 
        // Add panels..
        contentPane.add(allNotesPanel, BorderLayout.CENTER);
        contentPane.add(addNewNotePanel, BorderLayout.SOUTH);
 
        // Adjust properties and make the frame visible..
        setSize(600, 200);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setVisible(true);
    }
}

Notes
Views get data directly from the Model, and update model by using the Models interface. Views know nothing about user input..

Controller

AddNewNoteButton.java
package biz.tugay.swingynotes.controller;
 
import biz.tugay.swingynotes.view.AddNewNotePanel;
import biz.tugay.swingynotes.view.AllNotesPanel;
 
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AddNewNoteButton extends JButton implements ActionListener {
 
    private final AddNewNotePanel addNewNotePanel;
    private final AllNotesPanel allNotesPanel;
 
    public AddNewNoteButton(final AddNewNotePanel addNewNotePanel, final AllNotesPanel allNotesPanel) {
        super("Add New Note!");
        addActionListener(this);
 
        this.addNewNotePanel = addNewNotePanel;
        this.allNotesPanel = allNotesPanel;
    }
 
    @Override
    public void actionPerformed(ActionEvent e) {
        addNewNotePanel.addNote();
        allNotesPanel.displayAllNotes();
    }
}

Notes
The controller gets mouse click, and tells the related Views to what to do..

Entry Point

And finally App.java
package biz.tugay.swingynotes;
 
import biz.tugay.swingynotes.controller.AddNewNoteButton;
import biz.tugay.swingynotes.model.SwingyNoteService;
import biz.tugay.swingynotes.view.AllNotesPanel;
import biz.tugay.swingynotes.view.AddNewNotePanel;
import biz.tugay.swingynotes.view.SwingyNotesMainFrame;
 
import javax.swing.*;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class App {
 
    public static void main(String[] args) {
 
        // Model..
        final SwingyNoteService swingyNoteService = new SwingyNoteService();
 
        // View..
        final AllNotesPanel allNotesPanel = new AllNotesPanel(swingyNoteService);
        final AddNewNotePanel addNewNotePanel = new AddNewNotePanel(swingyNoteService);
 
        // Controller..
        final AddNewNoteButton addNewNoteButton = new AddNewNoteButton(addNewNotePanel, allNotesPanel);
        addNewNotePanel.add(addNewNoteButton);
 
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new SwingyNotesMainFrame(allNotesPanel, addNewNotePanel);
            }
        });
    }
}