MVC

Hello,

There are many topics regarding MVC. I like the following a lot.

However there are still a couple of issues that I feel uncertain of.
Should the Views register as observers to every Model entity or just the Model singleton?

  • In a game like Ludo (static and limited number of pieces) I would assume that each piece should be observed by the viewers. Each move would then notify the Views.

  • In an arcade game (Model entities are created and destroyed) I have registered the Views in the Model singleton only, and read all entities from the Model on notification. This works ok because all entities are updated in the Model every tick. In essence; all notifications are collated into a single notification.

  • In a RTS (Views do not observe the entire Model, and different Views observe different parts of the Model) I’m a bit lost. I would prefer if the Views are notified when something “interesting” occurs in their View.

Hi!

Sorry for my silly question but where are your controllers? Don’t forget the C in MVC.

I left out the Controllers for simplicity. :slight_smile:

I have other questions concerning the Controllers, but right now I’m focusing on the Model/Viewer part.
Right now there is one abstract Controller class (with an abstract execute() method) and one concrete class for every “verb” in the game logic (move, attach etc.).

When discussing MVC, you really cannot leave out Controllers for the sake of simplicity.

The Model and View are separated by the Controller.

Now I’m lost. I thought Viewers registered as observers in the Model.
Should the Views register as observers in the Controller?

You use then the pattern Observer whereas MVC is a mix of Observer, Composite and Strategy canonical design patterns. The controller handles the communication between the model and the view. The view is the representation of the data and the model contains the data. If you want, I can send you an example of very small application using MVC, it would allow you to understand the basics.

Here is an example of my apparently not so great MVC prowess…
I couldn’t find a place for a Controller in this Rock-Paper-Scissor game.
Can anyone refactor the code below to use a Controller?


package rps;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RPS {

	private enum Hand { Rock, Paper, Scissor }
	private enum Player { Attacker, Defender }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
	        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	    }
	    catch (UnsupportedLookAndFeelException e) {}
	    catch (ClassNotFoundException e) {}
	    catch (InstantiationException e) {}
	    catch (IllegalAccessException e) {}

	    new RPS();
	}

	private RPS() {
		Model model = new Model();
		new View(model, Player.Attacker);
		new View(model, Player.Defender);
	}

	// Model
	private class Model extends Observable {
		private int score;
		private Hand attacker;
		private Hand defender;

		public void setAttacker(Hand hand) {
			this.attacker = hand;
			setChanged();

			testVictory();
		}
		public void setDefender(Hand hand) {
			this.defender = hand;
			setChanged();

			testVictory();
		}

		// Test for Victory
		// Update score and reset attacker and defender
		public void testVictory() {
			if (attacker == null) return;
			if (defender == null) return;

			if ((attacker == Hand.Rock && defender == Hand.Scissor) ||
				(attacker == Hand.Paper && defender == Hand.Rock) ||
				(attacker == Hand.Scissor && defender == Hand.Paper)) {
				score++;
				notifyObservers(Player.Attacker);
			} else
			if ((defender == Hand.Rock && attacker == Hand.Scissor) ||
				(defender == Hand.Paper && attacker == Hand.Rock) ||
				(defender == Hand.Scissor && attacker == Hand.Paper)) {
				score--;
				notifyObservers(Player.Defender);
			}
			else notifyObservers();

			attacker = null;
			defender = null;
		}

		@Override
		public String toString() { return "[Model]"; }
	}

	// View
	private class View extends JFrame implements Observer {
		private static final long serialVersionUID = 1L;
		private static final String VICTORY = "Victory!";
		private static final String DEFEAT  = "Defeat!";
		private static final String DRAW    = "Draw!";
		private final Model model;
		private final Player player;
		private final JLabel score = new Label();
		private final JLabel info  = new Label();

		public View(Model model, Player player) {
			super(player.toString());
			this.model = model;
			this.player = player;
			model.addObserver(this);

			updateScore();
			add(score, BorderLayout.NORTH);
			add(new Panel());
			add(info, BorderLayout.SOUTH);

			setDefaultCloseOperation(EXIT_ON_CLOSE);
			setResizable(false);
			pack();
			setLocationByPlatform(true);
			setVisible(true);
		}

		@Override
		public void update(Observable o, Object note) {
			updateScore();
			if (note == null || note instanceof Player) {
				notifyWinner((Player) note);
			}
		}
		private void updateScore() {
			final int score = (player == Player.Attacker) ? model.score : -model.score;
			this.score.setText("Score: "+ score);
		}
		private void notifyWinner(Player player) {
			if (player == null) {
				info.setText(DRAW);
			} else if (player == this.player) {
				info.setText(VICTORY);
			} else {
				info.setText(DEFEAT);
			}
		}

		private class Panel extends JPanel {
			private static final long serialVersionUID = 1L;
			public Panel() {
				super(new GridLayout(1, 3));
				add(new Button(Hand.Rock));
				add(new Button(Hand.Paper));
				add(new Button(Hand.Scissor));
			}

			private class Button extends JButton implements ActionListener {
				private static final long serialVersionUID = 1L;
				private final Hand hand;

				public Button(Hand hand) {
					super(hand.toString());
					this.hand = hand;
					addActionListener(this);
				}

				@Override
				public void actionPerformed(ActionEvent ae) {
					if (View.this.player == Player.Attacker) {
						View.this.model.setAttacker(hand);
					} else {
						View.this.model.setDefender(hand);
					}
				}
			}
		}

		private class Label extends JLabel {
			private static final long serialVersionUID = 1L;
			public Label(String message) {
				super(message);
				setHorizontalAlignment(JLabel.CENTER);
				setFont(getFont().deriveFont(Font.BOLD));
			}
			public Label() {
				this("...");
			}
		}
	}
}

A ‘model’ shouldn’t contain logic:


// Test for Victory
		// Update score and reset attacker and defender
		public void testVictory() {
			if (attacker == null) return;
			if (defender == null) return;

			if ((attacker == Hand.Rock && defender == Hand.Scissor) ||
				(attacker == Hand.Paper && defender == Hand.Rock) ||
				(attacker == Hand.Scissor && defender == Hand.Paper)) {
				score++;
				notifyObservers(Player.Attacker);
			} else
			if ((defender == Hand.Rock && attacker == Hand.Scissor) ||
				(defender == Hand.Paper && attacker == Hand.Rock) ||
				(defender == Hand.Scissor && attacker == Hand.Paper)) {
				score--;
				notifyObservers(Player.Defender);
			}
			else notifyObservers();

			attacker = null;
			defender = null;
		}

Would this be a correct MVC design?
The Model is now stupid
The Controller is responsible for updating the Model
The Viewer is observing the Controller instead of the Model <-- is this correct MVC design?


package rps;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RPS2 {

	private enum Hand { Rock, Paper, Scissor }
	private enum Player { Attacker, Defender }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
	        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	    }
	    catch (UnsupportedLookAndFeelException e) {}
	    catch (ClassNotFoundException e) {}
	    catch (InstantiationException e) {}
	    catch (IllegalAccessException e) {}

	    new RPS2();
	}

	private RPS2() {
		Model model = new Model();
		Controller controller = new Controller(model);
		new View(controller, Player.Attacker);
		new View(controller, Player.Defender);
	}

	// Model
	private class Model {
		private int score;
		private Hand attacker;
		private Hand defender;

		public void setAttacker(Hand hand) {
			this.attacker = hand;
		}
		public void setDefender(Hand hand) {
			this.defender = hand;
		}

		@Override
		public String toString() { return "[Model]"; }
	}

	// Controller
	private class Controller extends Observable {
		private final Model model;

		public Controller(Model model) {
			this.model = model;
		}

		public void setHand(Player player, Hand hand) {
			if (player == Player.Attacker) {
				model.setAttacker(hand);
				setChanged();
			}
			if (player == Player.Defender) {
				model.setDefender(hand);
				setChanged();
			}
			testVictory();
		}


		// Test for Victory
		// Update score and reset attacker and defender
		private void testVictory() {
			if (model.attacker == null) return;
			if (model.defender == null) return;

			Player winner = null;
			if ((model.attacker == Hand.Rock    && model.defender == Hand.Scissor) ||
				(model.attacker == Hand.Paper   && model.defender == Hand.Rock)    ||
				(model.attacker == Hand.Scissor && model.defender == Hand.Paper)) {
				model.score++;
				winner = Player.Attacker;
			}
			if ((model.defender == Hand.Rock    && model.attacker == Hand.Scissor) ||
				(model.defender == Hand.Paper   && model.attacker == Hand.Rock)    ||
				(model.defender == Hand.Scissor && model.attacker == Hand.Paper)) {
				model.score--;
				winner = Player.Defender;
			}
			notifyObservers(winner);

			model.attacker = null;
			model.defender = null;
		}

		public int getScore(Player player) {
			if (player == Player.Attacker)
				return model.score;
			if (player == Player.Defender)
				return -model.score;
			return 0;
		}
		public Hand getHand(Player player) {
			if (player == Player.Attacker)
				return model.attacker;
			if (player == Player.Defender)
				return model.defender;
			return null;
		}

		@Override
		public String toString() { return "[Controller]"; }
	}

	// View
	private class View extends JFrame implements Observer {
		private static final long serialVersionUID = 1L;
		private static final String VICTORY = "Victory!";
		private static final String DEFEAT  = "Defeat!";
		private static final String DRAW    = "Draw!";
		private final Controller model;
		private final Player player;
		private final JLabel score = new Label();
		private final JLabel info  = new Label();

		public View(Controller controller, Player player) {
			super(player.toString());
			this.model = controller;
			this.player = player;
			controller.addObserver(this);

			updateScore();
			add(score, BorderLayout.NORTH);
			add(new Panel());
			add(info, BorderLayout.SOUTH);

			setDefaultCloseOperation(EXIT_ON_CLOSE);
			setResizable(false);
			pack();
			setLocationByPlatform(true);
			setVisible(true);
		}

		private void updateScore() {
			this.score.setText("Score: "+ model.getScore(player));
		}
		private void notifyWinner(Player player) {
			if (player == null) {
				info.setText(DRAW);
			} else if (player == this.player) {
				info.setText(VICTORY);
			} else {
				info.setText(DEFEAT);
			}
		}

		@Override
		public void update(Observable o, Object note) {
			System.out.println(this +" "+ o +" "+ note);
			updateScore();
			if (note == null || note instanceof Player) {
				notifyWinner((Player) note);
			}
		}

		@Override
		public String toString() { return "[View]"; }

		private class Panel extends JPanel {
			private static final long serialVersionUID = 1L;
			public Panel() {
				super(new GridLayout(1, 3));
				add(new Button(Hand.Rock));
				add(new Button(Hand.Paper));
				add(new Button(Hand.Scissor));
			}

			private class Button extends JButton implements ActionListener {
				private static final long serialVersionUID = 1L;
				private final Hand hand;

				public Button(Hand hand) {
					super(hand.toString());
					this.hand = hand;
					addActionListener(this);
				}

				@Override
				public void actionPerformed(ActionEvent ae) {
					View.this.model.setHand(player, hand);
				}
			}
		}

		private class Label extends JLabel {
			private static final long serialVersionUID = 1L;
			public Label(String message) {
				super(message);
				setHorizontalAlignment(JLabel.CENTER);
				setFont(getFont().deriveFont(Font.BOLD));
			}
			public Label() {
				this("...");
			}
		}
	}
}

I hate to say this, but what Mr_Light said is unfortunately not correct.

All logic is in the Model (!)

The View shows the UI and catches UI events.
The Controller processed input and calls the Model
The Model does everything (business rules are implemented here)

Example:

The Controller sends all events from the View to the Model.

  • the View can have a textfield
  • the View will call the Controller when [enter] is pressed in the textfield, and pass text to the Controller
  • the Controller will validate the input from the View
  • the Controller might need to convert/parse the input (like String -> int)
  • the Controller will call the method with the processed data

The term model-view-controller was made popular because some people realized it probably isn’t a good idea to be performing database queries in the middle of generating HTML (I’m looking at you, PHP guys!). But please don’t forget that design patterns are just common solutions to problems, they are not absolute laws that must be followed.

That said, I agree with Riven that the model should contain logic. Let’s take the following example:


public class PokerPlayer {
    private int age;

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

This would be a model class according to Mr. Light, it contains no logic. Now lets say that for some bizarre reason something calls setAge(-12). The model wouldn’t complain, but you would almost certainly have problems with this state elsewhere, since none of your other code will expect ages to be negative. Therefore (in my opinion) having no logic in the model is bad, the model should be responsible for its internal state (I think the fancy word is invariants). So, if the model would throw an IllegalArgumentException when the set age is negative, you would have the guarantee that when you use the PokerPlayer class its invariants are correct, and there is no need to double-check every time you use the class.

Um, that’s not ‘logic’ in the sense that it was used. In the context used, ‘logic’ means something which changes the model, so the logic is what calls ‘setAge()’. Calling it with a value of -12 is a logic failure - it should throw an exception, because then you know the logic has failed!

heh, not sure to who that was directed. Let me just state that I never mind being told that I’m wrong. Hell sometimes I even say things I know are wrong(not the case here) just to provoke a question and/or try to direct the other persons focus in a certain direction. I actually loath people who keep quiet just because they presume ‘I don’t get it’. There is also no need for misplaced ‘respect’.

Knowing that something is wrong is the first step to fixing things. It’s really hard to solve problems your not even aware about.

now, that being said… :slight_smile:

[quote]A ‘model’ shouldn’t contain logic:
[/quote]
Perhaps I should have said something along the lines of: ‘A model shouldn’t contain this kind of stuff:’.

Nah, ‘The actions performed by the model include activating business processes or changing the state of the model.’ http://java.sun.com/blueprints/patterns/MVC-detailed.html making the decision as to fire which business processes is business logic.

Now wikipedia saids:
“It is common to split an application into separate layers that run on different computers: the presentation/user interface (UI) (view), business logic (controller), and data access (model).”
which ‘hits home’ just as much as “all logic is in the Model”

As Mr Gol points out, but he uses a poor example. the problem that your having there is that your actually miss-using and int for age but it’s a lot more time effective then making an age class which can only represent valid ages.

A better example would be Person#isAdult(); which probably returns true or false based on a definition of other facts like age or marital status(as is the case here legally): so something like “return getAge() >= 18 || isMarried()” or perhaps as in other cultures “return hasPassedManhoodTrial()”.

Examining the latter it’s more a series of definitions (which might well be formed through logic*) which is kinda different from the stuff that was quoted there. moreover the type of testXXX kinda fundamental invites inconsistencies. if conditions are met for XXX then XXX needs to be true.
*every evaluation includes logic, using the word logic in the context of programming doesn’t narrow down - it’s kinda like saying ‘stuff’

I get that it was a poor and confusing reply, my bad.

No it should be observing the model; I kinda steered you in the wrong direction there.(sorry for that)

I think the problems come from naming your Model ‘model’, Controller ‘controller’ etc. If you go about it that way your modeling the solution for the problem of your code(wish I could find a better way to say this) You should model your code after The system your trying to represent:

While rock-paper-sissors has easy well understood logic capturing it and seperating stuff into MVC gets a little bit tricky if you add rounds to it.

So pick something like RockPaperSissorsBattle (or just battle) and Round or something.

Alike:

import java.util.ArrayList;
import java.util.List;


public class Battle {
	enum Hand { 
		Rock, Paper, Scissor; 
		
		boolean isBetterThan(Hand other) {
			return this.equals(Paper) && other.equals(Rock) || 
					this.equals(Scissor) && other.equals(Paper) ||
					this.equals(Rock) && other.equals(Scissor);			
		}
	}

	class Round {
		private Hand firstPlayersHand;
		private Hand secondPlayersHand;
		
		public void setFirstHand(Hand hand) {
			this.firstPlayersHand = hand;
		}
		
		public void setSecondHand(Hand hand) {
			this.firstPlayersHand = hand;
		}
		
		/**
		 * -1 if there's no winner 0 if it's a tie 1 if first player won or 2 if the second player won
		 * @return an int representing the outcome.
		 */
		public int getWinner() {
			if(!isFinised()) return -1;
			if(firstPlayersHand.equals(secondPlayersHand)) return 0;
			return firstPlayersHand.isBetterThan(secondPlayersHand) ? 1 : 2;
		}

		public boolean isFinised() {
			return firstPlayersHand != null && secondPlayersHand != null;
		}
	}
	
	private String firstPlayer; // Should perhaps be an actual palyer object
	private String secondPlayer; 
	private int bestOf;

	private List<Round> rounds = new ArrayList<Round>();
	
	public Battle(String firstPlayer, String secondPlayer, int bestOf) {
		this.firstPlayer = firstPlayer;
		this.secondPlayer = secondPlayer;
		this.bestOf = bestOf;
	}
	
	public Round getCurrentRound() {
		if(rounds.isEmpty()) return null;
		return rounds.get(rounds.size());
	}
	
	public void startNewRound() {
		rounds.add(new Round());
	}
		
	public String getWinner() {
		int roundsWonByFirstPlayer = 0;
		int roundsWonBySecondPlayer = 0;
		for(Round round: rounds) {			
			if(round.getWinner() == 1) {
				roundsWonByFirstPlayer++;
			} else if(round.getWinner() == 2) {
				roundsWonBySecondPlayer++;
			}
			
			if(roundsWonByFirstPlayer>=bestOf) {
				return firstPlayer;
			}
			if(roundsWonBySecondPlayer>=bestOf) {
				return secondPlayer;
			}
		}
		return null;
	}
}

if you add some observer stuff to it you should be able to get things working, simply ‘inspect’ the model on updates.

[quote=“Mr_Light,post:13,topic:33719”]
This is followed by an honest ‘wtf’ from me.

That’s totally not how I learned MVC! I can maybe blaim my education… then, for misleading you people in this thread.

I mean, it’s even in the word ‘Model’ => it models reality, so it contains all business logic. What I learnt is that the Controller simply retrofits/passes events to the Model, which then does everything.

When you’d take a website for example: the html/css/js in the browser is the View, the servlet handling the POST request is the controller, the Model performs all kinds of checks and finally writes them to a database (this is not part of the Model!)

Appearantly this is all wrong, as in ‘not pure MVC’, but it certainly works perfectly for me. I’d pull my hear out when I’d find business logic in a Controller. Oh well, it’s just a matter of definition I guess.

I agree with Riven. That wikipedia definition is not the one used by business, and would not be an OO design. The link to the Sun site is how MVC is used in the business world.

Well the wikipedia article didn’t bring about a wtf(though it was a close call) but it did leave me scratching my head here and there.

The sun article seems to be on the mark; but is very subtle and words where chosen very carefully. Separating business logic isn’t a particular goal of MVC it’s goal is to avoid code duplication and allowing to have multiply views. (Where to stick which kind of business logic is kinda a different discussion and how to separate it even more.) The logic that is in a controller is the logic that defines how a program responds to input. (look for the subtle difference ;))

Sidenote: The problem with MVC is also that everyone and their uncle uses (or claims to use) it(like singleton). MVC is probably even worse cause it usually ends up being a pillar for the design of the rest of the application - leaving it entangled with just about everything. A definite topic to avoid in job interviews. :slight_smile: