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
Observable
class andObserver
interface.
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 Observer
s with the Subject calling addObserver()
.
In the Application
constructor, you can add the following:
1 | game.addObserver(scorePanel); |
Info
Download the refactored SpaceInvaders.