Observer Pattern Exercise
Info
This exercise is based on a simple implementation of the SpaceInvaders game.
Problem¶
The ScorePanel is updated by the Application class every time the timer ticks. We want to change this situation so that ScorePanel is only updated when a change in the game warrants it (e.g., the number of invaders or missiles changes, or a new game starts).
Consider how to implement this functionality using the Observer design pattern.
Note
- You should remove the call to the
ScorePanel.update()method in theApplication.addTimer()method. - You should use Java API's
Observableclass andObserverinterface.
Solution¶
We need to decide "who" is the observer, and "who" is the subject of observation.
ScorePanel is the observer of the events in the game. The game events are managed in the controller class SpaceInvaderGame. So, ScorePanel must implement Observer interface and SpaceInvaderGame (which is the subject of observation) must extend the Observable class.
Next, identify the observable behavior in the subject (some behaviors that change the state, that you want the observers to be updated about; such behavior is called the trigger point).
There are several methods in the SpaceInvaderGame that can be considered as a trigger point; essentially any method that updates the number of missiles or the number of invaders in the game must be considered a trigger point.
Next, call (setChanged() and) the notifyObservers() method from the trigger point method.
Here is an example:
1 2 3 4 5 6 7 8 9 10 11 | // fires a missile if max number of missiles in play has // not been exceeded, otherwise silently returns private void fireMissile() { if (numMissilesInPlay < MAX_MISSILES) { Missile m = new Missile(tank.getX(), tank.getY()); sprites.add(m); numMissilesInPlay++; setChanged(); notifyObservers(); } } |
Next, decide how the observers are going to react to the notification (override the update method in each concrete observer)
Here is a sample implementation of update in ScorePanel:
1 2 3 4 5 6 7 8 | @Override public void update(Observable o, Object arg) { SpaceInvaderGame game = (SpaceInvaderGame) o; invadersLbl.setText(INVADERS_TXT + game.getNumInvadersDestroyed()); missilesLbl.setText(MISSILES_TXT + (SpaceInvaderGame.MAX_MISSILES - game.getNumMissiles())); repaint(); } |
Finally, register the Observers with the Subject calling addObserver().
In the Application constructor, you can add the following:
1 | game.addObserver(scorePanel); |
Info
Download the refactored SpaceInvaders.