Successfully refactored the 423-line BaseGame class into modular, composable utilities using composition over inheritance.
- 423 lines in a single class
- Mixed responsibilities (state, UI, scoring, storage)
- Difficult to test individual parts
- Hard to extend without modifying base class
- 265 lines in BaseGameRefactored (37% reduction)
- Separated concerns into focused utilities:
GameStateManager(95 lines) - State managementGameResultsManager(98 lines) - Scoring & resultsGameUIManager(208 lines) - UI lifecycle
- Total: Similar line count but much better organized
Each manager has one clear purpose:
// State Manager - Only handles game state
stateManager.incrementScore();
stateManager.nextRound();
// Results Manager - Only handles scoring/results
resultsManager.recordAnswer(answer, isCorrect);
resultsManager.calculateResult(state);
// UI Manager - Only handles UI components
uiManager.updateScore(score, total, streak);
uiManager.showResults(result);Each utility can be tested independently:
- Test state transitions without UI
- Test scoring logic without game flow
- Test UI updates without game logic
New features can be added to specific managers:
- Add new state fields → GameStateManager
- Add new metrics → GameResultsManager
- Add new UI components → GameUIManager
Subclasses only need to implement game-specific logic:
class MyGame extends BaseGameRefactored {
// Only implement these 5 methods:
protected generateScenarios() { }
protected renderScenario() { }
protected renderGame() { }
protected checkAnswer() { }
protected handleAnswerFeedback() { }
}Keep both BaseGame versions, migrate games one at a time:
- Keep existing games using original BaseGame
- New games use BaseGameRefactored
- Migrate existing games when updating them
Update all games at once:
- Update imports to use BaseGameRefactored
- Test each game thoroughly
- Remove original BaseGame
Less risky, allows testing in production with new games first.
/src/games
BaseGame.ts # Original (keep for now)
BaseGameRefactored.ts # New modular version
/src/lib
game-state-manager.ts # State management
game-results-manager.ts # Scoring & results
game-ui-manager.ts # UI lifecycle
- Maintainability - Clear separation of concerns
- Testability - Each piece can be tested in isolation
- Reusability - Managers can be used independently
- Flexibility - Easy to extend specific functionality
- Clarity - Each file has a single, clear purpose
// Before: Everything mixed in BaseGame
this.state.score++;
this.state.streak++;
this.scoreDisplay.incrementScore();
this.answers.push({...});
// After: Clear separation
this.stateManager.incrementScore();
this.uiManager.incrementScore();
this.resultsManager.recordAnswer(answer, isCorrect);- Test with one game - Try migrating NameThatHand first
- Gather feedback - See if the new structure is easier to work with
- Optimize further - Could extract more utilities if needed
- Add unit tests - Now that components are isolated
The refactoring maintains all functionality while providing much better code organization. The 37% reduction in BaseGame size and clear separation of concerns will make future development and maintenance significantly easier.