Interface PropertyChangeEnabledGameControls
- All Superinterfaces:
GameControls
- All Known Implementing Classes:
TetrisGame
PropertyChange
framework of the Observer Design Pattern to inform interested parties
of updates to the Tetris game state.
Defines behaviors allowing PropertyChangeListeners to be added or removed from a
GameControls object. Objects from implementing classes should inform added
PropertyChangeListeners when methods defined in GameControls mutate
the state of the object.
Event System Overview
This interface uses Java's PropertyChangeListener framework combined with modern
type-safe GameEvent objects. When game state changes, listeners receive
PropertyChangeEvents where the newValue contains a GameEvent record
with the actual event data.
Available Game Events
The following events can be observed, each as a sealed GameEvent subtype:
GameEvent.GameStateChanged(property name: "GameStateChanged") - Fired when game transitions between NEW, RUNNING, PAUSED, or OVER statesGameEvent.NextPieceChanged(property name: "NextPieceChanged") - Fired when a new next piece is selected (typically after current piece freezes)GameEvent.CurrentPieceChanged(property name: "CurrentPieceChanged") - Fired when the current piece moves, rotates, or a new piece spawnsGameEvent.FrozenBlocksChanged(property name: "FrozenBlocksChanged") - Fired when a piece freezes into place on the boardGameEvent.RowsCleared(property name: "RowsCleared") - Fired when one or more complete rows are cleared from the board
Usage Patterns
Pattern 1: Listen to All Events
Register a listener without specifying a property name to receive all events. Use pattern matching to handle different event types:
TetrisGame game = new TetrisGame();
game.addPropertyChangeListener(evt -> {
if (evt.getNewValue() instanceof GameEvent event) {
switch (event) {
case GameEvent.GameStateChanged e ->
System.out.println("State: " + e.oldState() + " -> " + e.newState());
case GameEvent.CurrentPieceChanged e ->
updateDisplay(e.piece());
case GameEvent.RowsCleared e ->
updateScore(e.count());
case GameEvent.NextPieceChanged e ->
updateNextPiecePreview(e.piece());
case GameEvent.FrozenBlocksChanged e ->
redrawBoard(e.blocks());
}
}
});
Pattern 2: Listen to Specific Events
Register a listener with a specific property name to receive only that event type.
The property name is the simple class name of the GameEvent subtype:
// Listen only for game state changes
game.addPropertyChangeListener("GameStateChanged", evt -> {
if (evt.getNewValue() instanceof GameEvent.GameStateChanged e) {
if (e.newState() == GameControls.GameState.OVER) {
showGameOverDialog();
}
}
});
// Listen only for rows cleared
game.addPropertyChangeListener("RowsCleared", evt -> {
if (evt.getNewValue() instanceof GameEvent.RowsCleared e) {
playSound("row_clear.wav");
addScore(e.count() * 100);
}
});
Pattern 3: Combined Approach
Use multiple specific listeners for better organization and performance:
// Separate concerns into different listeners
game.addPropertyChangeListener("CurrentPieceChanged", this::handlePieceMovement);
game.addPropertyChangeListener("GameStateChanged", this::handleStateChange);
game.addPropertyChangeListener("RowsCleared", this::handleScoring);
Event Timing and Order
When multiple events are fired from a single action (e.g., drop()), they are fired
in a specific order. See individual method documentation in TetrisGame for
details on which events are fired and in what order.
Type Safety Benefits
Unlike traditional string-based property changes, this system provides:
- Compile-time type checking with pattern matching
- IDE autocomplete for event types and their data fields
- Exhaustiveness checking in switch expressions
- Immutable event data via records
- No casting required when using pattern matching
- Version:
- Autumn 2025
- Author:
- Charles Bryan
- See Also:
-
Nested Class Summary
Nested classes/interfaces inherited from interface GameControls
GameControls.Block, GameControls.FrozenBlocks, GameControls.GameState, GameControls.IndividualPiece, GameControls.Point -
Method Summary
Modifier and TypeMethodDescriptionvoidaddPropertyChangeListener(PropertyChangeListener theListener) Add a PropertyChangeListener to the listener list.voidaddPropertyChangeListener(String thePropertyName, PropertyChangeListener theListener) Add a PropertyChangeListener for a specific property.voidremovePropertyChangeListener(PropertyChangeListener theListener) Remove a PropertyChangeListener from the listener list.voidremovePropertyChangeListener(String thePropertyName, PropertyChangeListener theListener) Remove a PropertyChangeListener for a specific property.
-
Method Details
-
addPropertyChangeListener
Add a PropertyChangeListener to the listener list. The listener is registered for all properties. The same listener object may be added more than once, and will be called as many times as it is added. If listener is null, no exception is thrown and no action is taken.- Parameters:
theListener- The PropertyChangeListener to be added
-
addPropertyChangeListener
Add a PropertyChangeListener for a specific property. The listener will be invoked only when a call on firePropertyChange names that specific property. The same listener object may be added more than once. For each property, the listener will be invoked the number of times it was added for that property. If propertyName or listener is null, no exception is thrown and no action is taken.- Parameters:
thePropertyName- The name of the property to listen on.theListener- The PropertyChangeListener to be added
-
removePropertyChangeListener
Remove a PropertyChangeListener from the listener list. This removes a PropertyChangeListener that was registered for all properties. If listener was added more than once to the same event source, it will be notified one less time after being removed. If listener is null, or was never added, no exception is thrown and no action is taken.- Parameters:
theListener- The PropertyChangeListener to be removed
-
removePropertyChangeListener
Remove a PropertyChangeListener for a specific property. If listener was added more than once to the same event source for the specified property, it will be notified one less time after being removed. If propertyName is null, no exception is thrown and no action is taken. If listener is null, or was never added for the specified property, no exception is thrown and take no action.- Parameters:
thePropertyName- The name of the property that was listened on.theListener- The PropertyChangeListener to be removed
-