Flyweight Space Invaders

This is something I wrote for a final-year object-oriented design course. I haven't bothered placing the class diagrams here yet but the code is here, as is the project in applet form. If you just want to see this thing in action, take a look at the applet.

Introduction

The project is inspired by the arcade game "Space Invaders". However, it is non-interactive. The user may choose how many rows and columns of alien ships appear in the game. Missiles automatically fire every few seconds from earth. The aliens do not fire back. If all aliens are destroyed or if they reach the bottom of the window the game terminates. The user may restart the game or exit.

The design pattern employed is the flyweight pattern. This was first formally discussed in "Design Patterns" by Gamma, et al.

Space Invaders seemed an ideal candidate for learning the concept of the flyweight pattern. A relatively large number of alien ships must be created. These ships are composed of two colours each. Each ship is either still intact or has been hit by a missile. The grid-like property of the ships' layout means that their onscreen position is readily calculated from their index within the grid. There is potential for sharing much information between Ship objects.

Clearly, 64 ships is not a large number of objects. However, any memory saving is always desirable and, regardless, the intention is to demonstrate the use of the pattern. Flyweight is here shown as simple to implement.

Demo

Press start to run a new game. The size of alien invasion fleet is adjusted by the two sliders. To restart a game, first stop and then press start again.

Design Description

Intrinsic state consists of two colours. These colours are stored by a FlyweightShip object. This object is an abstract class. It also declares methods for calculating extrinsic state to be defined by subclasses. One such subclass is supplied; FlyweightShipImpl. The extrinsic state in question is the ship's onscreen position. This is calculated from a row and column index. The FlyweightShipFactory has sole responsibility for creating FlyweightShip objects. It returns FlyweightShipImpl objects.

The Ship class is implemented as a flyweight. It maintains a reference to a FlyweightShip for its colour and position information. Additionally, it records whether the ship is intact or has been hit by an earth missile. This is the only extrinsic state which cannot be implicitly calculated and it must be stored. An alien invasion fleet is represented by an InvasionFleet object. The constructor must be passed the desired size of the fleet; number of rows and columns. A number (rows times columns) of ships is then created. Two colours are picked for each; a corresponding flyweight is requested from the factory. A new ship is created, with a reference to the returned flyweight.

In the language of flyweight, there are several notional classes involved. Here, FlyweightShip corresponds to Flyweight. This only stores intrinsic state. FlyweightShipImpl is the ConcreteFlyweight. This extends Flyweight with methods for calculating extrinsic state. FlyweightShipFactory is the Factory class with responsibility for ensuring optimum memory storage for flyweight objects. Ship is the class which is implemented as a flyweight. InvasionFleet is the Client. It creates a two dimensional array of Ship objects. The row and column index of each ship maps to a ship's position within the fleet of ships and this is passed to the getX() and getY() methods for retrieving their onscreen position.

Apart from the flyweight, some design features should be noted.

The factory is implemented as a singleton. The intention was to eliminate accidental creation of multiple factories, which independently may create duplicate FlyweightShipImpl objects. The constructor on FlyweightShipImpl is package (a.k.a. "friendly") so that only the Factory may (properly) create these objects.

A cut-down form of the Observer pattern is also in evidence; both SpaceInvadersPanelListener and InvasionFleetListener are interfaces which may be implemented in order to receive notificatino of changes in SpaceInvadersPanel and InvasionFleet objects, respectively. For the sake of brevity there may be only one listener on each of these types.