Two Swing-based applications demonstrating the Model-View-Controller (MVC) architectural pattern combined with the Observer and Adapter patterns.
This package contains two MVC applications:
-
DJ BeatBox - A MIDI-based beat machine that plays drum beats, displays the current BPM and a visual beat indicator, and lets the user control playback via a menu and BPM adjustment buttons.
-
Heart Monitor - A heart rate monitoring simulator that uses the same UI infrastructure as the DJ BeatBox but adapts a simple heart model to work with the existing view. Demonstrates the Adapter pattern.
The core architectural pattern, splitting the application into three distinct roles:
| Role | Class | Responsibility |
|---|---|---|
| Model | BeatModel |
Manages MIDI sequencer state and BPM; notifies observers of changes |
| View | DJView |
Renders the UI; reads state from the model; sends user actions to controller |
| Controller | BeatController |
Responds to user input; translates it into model operations; updates view state |
Flow:
User Input → Controller → Model (state change)
↓
Observer notification
↓
View updates
The controller holds references to both the model and the view. The view holds a reference to both the model (to read state) and the controller (to forward user actions). The model knows nothing about the view or controller — it only notifies registered observers.
Used to decouple the model from the view. There are two separate observer channels:
BeatObserver— notified on every beat tick (firesupdateBeat())BPMObserver— notified whenever the BPM value changes (firesupdateBPM())
DJView implements both interfaces and registers itself with the model at construction time. BeatBar also implements BeatObserver to animate the progress bar independently.
Structure:
BeatModelInterface
├── registerObserver(BeatObserver)
├── registerObserver(BPMObserver)
├── removeObserver(BeatObserver)
└── removeObserver(BPMObserver)
BeatModel (subject)
├── notifies BeatObservers → DJView.updateBeat(), BeatBar.updateBeat()
└── notifies BPMObservers → DJView.updateBPM()
The HeartAdapter class demonstrates the Adapter pattern by wrapping the HeartModel (which has a different interface) to make it compatible with BeatModelInterface. This allows the heart monitoring system to reuse the existing DJView without modification.
Structure:
HeartModel (incompatible interface)
↓
HeartAdapter (adapts to BeatModelInterface)
↓
HeartController → DJView (works with BeatModelInterface)
Key adaptations:
HeartModel.getHeartRate()→BeatModelInterface.getBPM()HeartModel.setHeartRate()→BeatModelInterface.setBPM()- Timer-based heartbeat simulation instead of MIDI sequencer
Controllers program to the BeatModelInterface and ControllerInterface abstractions rather than concrete classes. Different model or controller implementations can be swapped in without changing the view or test drive.
DJTestDrive Entry point
│
├── BeatModelInterface Model contract
│ └── BeatModel MIDI sequencer, observer lists, BPM state
│
├── ControllerInterface Controller contract
│ └── BeatController Wires model and view; handles start/stop/BPM
│
├── DJView Swing UI (view + observer)
│ └── BeatBar Animated JProgressBar (beat observer)
│
├── BeatObserver Observer interface — beat ticks
└── BPMObserver Observer interface — BPM changes
HeartTestDrive Entry point
│
├── HeartModel Timer-based heart rate simulation
│ └── HeartAdapter Adapts HeartModel to BeatModelInterface
│
├── ControllerInterface Controller contract
│ └── HeartController Wires adapter and view; handles start/stop/heart rate
│
└── DJView (reused) Same Swing UI as DJ BeatBox
From the root of the project:
javac -d out MVCPattern/DJExample/*.javajava -cp out MVCPattern.DJExample.DJTestDriveTwo windows will open:
- My BeatBox — displays current BPM label and the animated beat bar, with a "DJ Control" menu containing Start/Stop items
- DJ Control — BPM input field and
+/Set/-buttons
Using the DJ BeatBox:
- Select DJ Control → Start from the menu bar to begin playback at 90 BPM
- Use the + and - buttons to nudge the BPM up or down by 1
- Type a value into the text field and press Set to jump to a specific BPM
- Select DJ Control → Stop to stop playback
- Close either window to exit
java MVCPattern.DJExample.HeartTestDriveTwo windows will open (identical UI to DJ BeatBox):
- My BeatBox — displays current heart rate and the animated beat bar
- DJ Control — Heart rate input field and
+/Set/-buttons
Using the Heart Monitor:
- Select DJ Control → Start from the menu bar to begin heartbeat simulation at 72 BPM (default heart rate)
- Use the + and - buttons to adjust heart rate by 1
- Type a value into the text field and press Set to jump to a specific heart rate
- The beat bar flashes with each heartbeat based on the current heart rate
- Select DJ Control → Stop to halt the heartbeat simulation
- Close either window to exit
Pros:
- Clear separation of concerns — UI, business logic, and coordination are independent
- The model can be tested without a UI
- The view can be redesigned without touching the model
- Multiple views of the same model are straightforward to add
Cons:
- More classes and indirection than a simple single-class UI
- The view still holds a reference to the model for direct state reads, which creates some coupling
- For very simple UIs the overhead of three layers may not be worth it
Pros:
- The model has zero knowledge of its views — fully decoupled
- Multiple independent observers can react to the same event
- Observers can be added or removed at runtime
Cons:
- Observers are notified in an unspecified order
- If an observer is not removed when no longer needed it will be held in memory (potential leak)
- Cascading or unexpected update chains can be hard to debug
Pros:
- Allows incompatible interfaces to work together without modifying existing code
- Enables reuse of existing components (e.g.,
DJViewworks with bothBeatModelandHeartModel) - Follows the Open/Closed Principle — open for extension, closed for modification
Cons:
- Adds an extra layer of indirection
- Can make the code harder to understand if overused
- Performance overhead from delegation (though typically negligible)
- MVC separates what the data is (Model) from how it looks (View) and what the user can do (Controller)
- Observer pattern is the mechanism that keeps the View in sync with the Model without the Model knowing anything about the View
- Adapter pattern enables incompatible interfaces to work together, as demonstrated by
HeartAdaptermakingHeartModelcompatible withBeatModelInterface - Programming to interfaces (
BeatModelInterface,ControllerInterface) makes each layer replaceable independently - Pattern synergy - combining MVC + Observer + Adapter allows maximum code reuse (same view works with both MIDI beats and heart simulation)