SwingyNotes design modified..

Initial is here!

This time I tried to follow Oracle 's explanations / guidelines so I modified the Swingy Notes a little. Trying to summarise what Oracle has to say..


Views

The view registers as a listener on the model. If the model data changes, the view must update its presentation as needed. Any changes to the underlying data of the model immediately result in a broadcast change notification, which the view receives.

Controllers

The controller is bound to the view. This typically means that any user actions (such as mouse clicks) that are performed on the view will invoke a registered listener method in the controller class. The controller is given a reference to the underlying model.

Sequence of Actions

Once a user interacts with the view, the following actions occur:
  1. The view recognises that a GUI action -- for example, pushing a button or dragging a scroll bar -- has occurred, using a listener method that is registered to be called when such an action occurs.
  2. The view calls the appropriate method on the controller.
  3. The controller accesses the model, possibly updating it in a way appropriate to the user's action.
  4. If the model has been altered, it notifies interested listeners, such as the view, of the change.

I think #1 and #2 are done implicitly by the Swing library.. But the bigger question I have is: How do we pass parameters to the appropriate method on the controller from the View in step 2?

Model

SwingyNoteService.java
package biz.tugay.swingynotes.model;
 
import java.util.*;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public final class SwingyNoteService extends Observable {
 
    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);
        setChanged();
        notifyObservers();
    }
 
    public Collection<SwingyNote> getAllNotes() {
        return swingyNotes;
    }
}

Notes
I do not like extending Observable, which means I can not extend anything else if needed.. And why is the Model responsible for notifying anyone? I do not like this approach..

View

AddNewNotePanel.java
package biz.tugay.swingynotes.view;
 
import javax.swing.*;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AddNewNotePanel extends JPanel implements Observer {
 
    private final JTextField newNoteInputTextField;
    private final JButton addNewNoteButton;
 
    public AddNewNotePanel() {
        this.newNoteInputTextField = new JTextField(20);
        this.addNewNoteButton = new JButton("Add new Note!");
 
        add(newNoteInputTextField);
        add(addNewNoteButton);
    }
 
    public void registerListenerOnAddNewNoteButton(ActionListener actionListener) {
        addNewNoteButton.addActionListener(actionListener);
    }
 
    @Override
    public void update(Observable o, Object arg) {
        newNoteInputTextField.setText(null);
    }
}

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;
import java.util.Observable;
import java.util.Observer;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AllNotesPanel extends JPanel implements Observer {
 
    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;
    }
 
    @Override
    public void update(Observable o, Object arg) {
        displayAllNotes();
    }
}

SwingNotesMainFrame.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
Implementing Observable is fine, but the method public void update(Observable o, Object arg), which is being called by the Observer pattern is not very useful is it? What am I supposed to do with the arguments I get? Cast to what?

Controller

AddNewButtonActionListener.java
package biz.tugay.swingynotes.controller;
 
import biz.tugay.swingynotes.model.SwingyNote;
import biz.tugay.swingynotes.model.SwingyNoteService;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
/* User: koray@tugay.biz Date: 2017/05/12 */
public class AddNewNoteButtonActionListener implements ActionListener {
 
    private final SwingyNoteService swingyNoteService;
 
    public AddNewNoteButtonActionListener(final SwingyNoteService swingyNoteService) {
        this.swingyNoteService = swingyNoteService;
    }
 
    @Override
    public void actionPerformed(final ActionEvent e) {
        final JButton sourceButton = (JButton) e.getSource();
        final Container buttonContainer = sourceButton.getParent();
        final JTextField relatedTextField = (JTextField) buttonContainer.getComponent(0);
        final String note = relatedTextField.getText();
        final SwingyNote swingyNote = new SwingyNote(note);
        swingyNoteService.addNote(swingyNote);
    }
}

Notes
Is this even how you get data from the View in the Controller?

Entry Point

package biz.tugay.swingynotes;
 
import biz.tugay.swingynotes.controller.AddNewNoteButtonActionListener;
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();
 
        // Controller..
        final AddNewNoteButtonActionListener addNewNoteButtonActionListener = new AddNewNoteButtonActionListener(swingyNoteService);
 
        // Bound this Controller to the relevant View..
        addNewNotePanel.registerListenerOnAddNewNoteButton(addNewNoteButtonActionListener);
 
        // Views observing Model change..
        swingyNoteService.addObserver(allNotesPanel);
        swingyNoteService.addObserver(addNewNotePanel);
 
        // Start the application..
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new SwingyNotesMainFrame(allNotesPanel, addNewNotePanel);
            }
        });
    }
}

So which implementation do you like more?