MVC desktop - where is the Controller?

Asked

Viewed 101 times

0

I modified it this example of MVC to try to make it more like my MVC vision (in which the role of the Controller is not very clear). I ended up with a program that only has Views and Model, without Controller. Where the Controller should be?

One thing that surprised me was that I created a behavior of observing the state of the view. My idea was that there would be only observation of the state of the Model, then I realized that I needed to implement the view state observation (method OneOfTheViews.update()). This behavior is expected to occur in the MVC?

Another relatively unexpected thing was that the views are nested, that is, there is a MainPanel that is the mother of other views. I don’t know if this is right in MVC or not.

P.S.: The program was set up with the short classes, right? Point for MVC :D

MVC Game

Mvcgame.java

package piovezan.mvcdesktop_trashgod;

import java.awt.EventQueue;

import javax.swing.JFrame;

import piovezan.mvcdesktop_trashgod.view.MainPanel;

public class MvcGame implements Runnable {

    public static void main(String[] args) {
        // Componentes Swing devem ser construídos no EDT (event-dispatching thread).
        EventQueue.invokeLater(new MvcGame());
    }

    @Override
    public void run() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new MainPanel().asComponent());
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

Piece java.

package piovezan.mvcdesktop_trashgod.model;

import java.awt.Color;

public enum Piece {
    RED(Color.red),
    GREEN(Color.green),
    BLUE(Color.blue);

    private final Color color;

    private Piece(Color color) {
        this.color = color;
    }

    public Color getColor() {
        return color;
    }
}

Java model.

package piovezan.mvcdesktop_trashgod.model;

import java.util.Observable;
import java.util.Random;

/**
 * Model
 */
public class Model extends Observable {

    private static final Random RAND_NUM_GEN = new Random();
    private Piece hidden = init();

    private Piece init() {
        Piece[] pieces = Piece.values();
        return pieces[RAND_NUM_GEN.nextInt(pieces.length)];
    }

    public void reset() {
        hidden = init();
        setChanged();
        notifyObservers();
    }

    public void check(Piece guess) {
        setChanged();
        notifyObservers(guess.equals(hidden));
    }
}

Java mainpanel.

package piovezan.mvcdesktop_trashgod.view;

import java.awt.BorderLayout;

import javax.swing.JLabel;
import javax.swing.JPanel;

import piovezan.mvcdesktop_trashgod.model.Model;

public final class MainPanel {

    private final JPanel mainPanel; 

    public MainPanel() {
        mainPanel = new JPanel(new BorderLayout());

        Model model = new Model();

        JLabel label = new JLabel("Guess what color!", JLabel.CENTER);
        mainPanel.add(label, BorderLayout.NORTH);

        OneOfTheViews view = new OneOfTheViews(model);
        mainPanel.add(view.asComponent(), BorderLayout.CENTER);

        ControlPanel controlPanel = new ControlPanel(model);
        mainPanel.add(controlPanel.asComponent(), BorderLayout.SOUTH);
    }

    public JPanel asComponent() {
        return mainPanel;
    }
}

Oneoftheviews.java

package piovezan.mvcdesktop_trashgod.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JLabel;
import javax.swing.JPanel;

import piovezan.mvcdesktop_trashgod.model.Model;
import piovezan.mvcdesktop_trashgod.model.Piece;

/**
 * View
 */
public class OneOfTheViews {

    private static final String TEXT_CLICK_A_BUTTON = "Click a button.";

    private final JPanel panel = new JPanel(new BorderLayout());
    private final ColorRoundIcon icon = new ColorRoundIcon(80, Color.gray);
    private final JLabel labelMoveOutcome = initLabel(); 
    private final Model model;

    public OneOfTheViews(Model model) {
        this.model = model;
        model.addObserver(new ModelObserver());
        panel.add(labelMoveOutcome, BorderLayout.CENTER);
        panel.add(genButtonPanel(), BorderLayout.SOUTH);
    }

    public JPanel asComponent() {
        return panel;
    }

    private JLabel initLabel() {
        JLabel label = new JLabel(TEXT_CLICK_A_BUTTON, icon, JLabel.CENTER);
        label.setVerticalTextPosition(JLabel.BOTTOM);
        label.setHorizontalTextPosition(JLabel.CENTER);
        return label;
    }

    private JPanel genButtonPanel() {
        JPanel panel = new JPanel();
        for (Piece piece : Piece.values()) {
            PieceButton pieceButton = new PieceButton(piece, this);
            panel.add(pieceButton.asComponent());
        }

        return panel;
    }

    public void update(Piece piece) {
        icon.setColor(piece.getColor());
        labelMoveOutcome.repaint();
        model.check(piece);
    }

    private class ModelObserver implements Observer {

        @Override
        public void update(Observable o, Object arg) {
            if (arg == null) {
                labelMoveOutcome.setText(TEXT_CLICK_A_BUTTON);
                icon.setColor(Color.gray);
            } else if ((Boolean) arg) {
                labelMoveOutcome.setText("Win!");
            } else {
                labelMoveOutcome.setText("Keep trying.");
            }
        }
    }
}

Controlpanel.java

package piovezan.mvcdesktop_trashgod.view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;

import piovezan.mvcdesktop_trashgod.model.Model;

/**
 * Control panel
 */
public class ControlPanel {

    private static final String RESET_LABEL = "Reset";

    private final Model model;
    private final JPanel panel = new JPanel();
    private final JButton resetButton = new JButton(RESET_LABEL);

    public ControlPanel(Model model) {
        this.model = model;

        panel.add(resetButton);
        resetButton.addActionListener(new ResetButtonHandler());
    }

    public JPanel asComponent() {
        return panel;
    }

    private class ResetButtonHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            String cmd = e.getActionCommand();
            if (RESET_LABEL.equals(cmd)) {
                model.reset();
            }
        }
    }
}

Colorroundicon.java

package piovezan.mvcdesktop_trashgod.view;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.Objects;

import javax.swing.Icon;

public final class ColorRoundIcon implements Icon {

    private final int size;
    private Color color;

    public ColorRoundIcon(int size, Color color) {
        this.size = size;
        this.color = color;
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(color);
        g2d.fillOval(x, y, size, size);
    }

    @Override
    public int getIconWidth() {
        return size;
    }

    @Override
    public int getIconHeight() {
        return size;
    }

    public void setColor(Color color) {
        this.color = Objects.requireNonNull(color);
    }
}

Piecebutton.java

package piovezan.mvcdesktop_trashgod.view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

import piovezan.mvcdesktop_trashgod.model.Piece;

public final class PieceButton {

    private final JButton pieceButton;
    private final Piece piece;
    private final OneOfTheViews view;

    public PieceButton(Piece piece, OneOfTheViews view) {
        this.piece = piece;
        this.view = view;
        this.pieceButton = new JButton(new ColorRoundIcon(16, piece.getColor()));
        this.pieceButton.addActionListener(new PieceButtonHandler());
    }

    public Piece getPiece() {
        return piece;
    }

    public JButton asComponent() {
        return pieceButton;
    }

    private class PieceButtonHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            view.update(piece);
        }
    }
}

2 answers

0


The role of the Controller is to receive the inputs from the user from the View, validate the data if this is the case and pass to the Model. Then notify the View of changes to the Model if the Model no longer has an Observer mechanism to do so (as asked in the question).

In the example of the question what is missing is a Controller to intermediate the user input (button pressing) of a given View that leads to the call of a method of that Controller instead of directly calling the method model.reset(), for example. So adding a Controller would look something like this:

public class ControlPanel {

    (...)

    private class ResetButtonHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            String cmd = e.getActionCommand();
            if (RESET_LABEL.equals(cmd)) {
                controller.reset();
            }
        }
    }
}

The controller (too simple) would be:

Java controller.

package piovezan.mvcdesktop_trashgod.controller;

import java.util.Objects;

import piovezan.mvcdesktop_trashgod.model.Model;

public class ControlPanelController {

    private final Model model;
    
    public ControlPanelController(Model model) {
        this.model = Objects.requireNonNull(model);
    }
    
    public void reset() {
        model.reset();
    }
}

I’m assuming the premise that the number of Controllers is one per View.

-1

I’ll try to explain how little I know about MVC. MVC is a software architecture standard, which separates the application into MODEL (responsible for data manipulation), VIEW (responsible for user interaction), and CONTROLLER (responsible for receiving user requests send to MODEL and relay to VIEW or the manager). Viewing your project (the image identifies the controller as the red, green, blue and reset buttons), on these buttons are the requests that will be sent to the MODEL. MODEL analyzes and returns what was requested (within the conditions).

Obs.: a good practice would be not to leave implementations in the model classes, for this I usually implement an extra layer between the model and the controller, in this case it is a SERVICE layer(used to store and validate business rules by removing both controller and model rules).

I hope I helped a little!

  • Your answer went out of the question. OP wanted to know where the controller is and you just tried to explain what the MVC is. It didn’t help much.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.