A Swing workout tracker with a layered architecture, an Observer-driven UI, and JSON persistence — built solo to replace a Notes-app habit that wasn't cutting it.
A few months into training, I was logging sets and reps in my phone's Notes app — duration, equipment, weight, sets, scrawled across a wall of unstructured text. It was tedious enough that I'd skip logging altogether, which defeated the point. Every fitness app I tried was either paywalled or buried in ads for what should be a simple structured record. Gym Bro is the tool I actually wanted: log a session, break it into cardio or lifting exercises, and see the totals.
Three packages, each with one job: model owns the domain and knows
nothing about Swing; persistence knows how to turn that domain into JSON and back;
ui is the only layer allowed to import javax.swing.
The original design manually refreshed the UI after every mutation — a classic tight-coupling smell where the model can't change without every call site remembering to babysit a label. I replaced that with an Observer: the model owns notification, the UI just reacts.
workoutRecords.addWorkoutSession(session); updateTotalWorkoutLabel(); // easy to forget addWorkoutFrame.dispose();
// WorkoutRecords.addWorkoutSession(session) workoutSessions.add(session); EventLog.getInstance().logEvent(event); notifyObservers(); // GUI refreshes itself
Every model class that needs to survive a restart implements a single
Writable interface (toJson()), so JsonWriter and
JsonReader don't need to know about cardio vs. weight exercises —
polymorphism does the branching.
{"workoutRecords": [{
"totalDuration": 300,
"listOfExercise": [
{ "duration": 100, "exerciseType": "model.Cardio", "cardioType": "ELLIPTICAL" },
{ "liftType": "BENCH_PRESS", "sets": 4, "exerciseType": "model.LiftWeight", "weight": 300 }
]
}]}
26 JUnit 5 tests across 9 classes cover domain logic and JSON round-trips; Checkstyle enforces style on every class.
A small design system inside GymBroGUI — one accent color,
one type scale, one button style — replaces what was originally bare
GridLayout and default-look buttons. Click through it below: it runs the
same total-hours math, session filtering, and event log as the real model classes,
not just static screenshots.
Track and view your workout effortlessly.
This is the actual menu tree, button copy, and window titles from GymBroGUI.java — wired up in JS for this page.