Publisher / Subscriber• Patterns: Observer, Strategy, Factory
Cricbuzz
Medium
Problem Summary
Design a high-scale Live Cricket Commentary and Scoreboard engine broadcasting ball-by-ball events, player statistics updates, and Duckworth-Lewis-Stern target projections in real time.
Functional Scope
- Ball-by-ball event processing: runs, wickets, extras (wides, no-balls), reviews.
- Real-time pub-sub broadcasting to millions of fan clients via WebSockets and push notifications.
- Dynamic target projection strategies (Run Rate extrapolation, DLS method).
- Per-player batting and bowling live statistics accumulation.
- Auditing and compensation support for DRS umpire reversals.
Entity-Relationship (ER) Schema
Match [1] --> [2] Innings Innings [1] --> [*] Over Over [1] --> [6..*] BallEvent Match [1] --> [*] ScoreboardObserver Match [1] --> [1] TargetCalculatorStrategy
Design Approach
Implement the Observer pattern to decouple ball-by-ball state mutations from downstream delivery mechanisms (WebSockets, mobile push, and search indexing). Protect match score mutations with thread-safe collections or actor queues.
Core Classes & Models
Match (Context managing teams, current innings, and observer registry)Innings (Tracks runs, wickets, overs, and batting/bowling lineups)BallEvent (Type, batsman, bowler, runs, wicket details)ScoreboardObserver (WebSocket broadcaster, Push notification dispatcher, Analytics logger)TargetCalculatorStrategy (DLSTargetStrategy, SimpleRunRateStrategy)
Code Blueprint
75 lines/**
* Low-Level Design: Cricbuzz Live Cricket Scoreboard
* Design Patterns Applied:
* - Observer Pattern: Real-time ball-by-ball score and wicket broadcasts to thousands of subscribers
* - Strategy Pattern: Dynamic Net Run Rate (NRR) and strike-rate calculation strategies
* - Factory Pattern: Match, Innings, and Over object generation
*/
import java.util.*;
// --- [DESIGN PATTERN: Observer] Observer for live score updates ---
interface ScoreSubscriber {
void onScoreUpdated(int runs, int wickets, double overs, String commentary);
}
class MobileAppSubscriber implements ScoreSubscriber {
private final String userId;
public MobileAppSubscriber(String userId) { this.userId = userId; }
@Override
public void onScoreUpdated(int runs, int wickets, double overs, String commentary) {
System.out.printf("📱 [App User: %s] %d/%d (%.1f ov) -> %s%n", userId, runs, wickets, overs, commentary);
}
}
// --- [DESIGN PATTERN: Strategy] Run Rate calculation algorithm ---
interface RunRateStrategy {
double calculate(int totalRuns, int ballsBowled);
}
class CurrentRunRateStrategy implements RunRateStrategy {
@Override
public double calculate(int totalRuns, int ballsBowled) {
if (ballsBowled == 0) return 0.0;
return (totalRuns * 6.0) / ballsBowled;
}
}
// --- [DESIGN PATTERN: Subject / Match Coordinator] ---
class MatchScoreboard {
private int runs = 0;
private int wickets = 0;
private int totalBalls = 0;
private final List<ScoreSubscriber> subscribers = new ArrayList<>();
private final RunRateStrategy crrStrategy = new CurrentRunRateStrategy();
public void subscribe(ScoreSubscriber sub) { subscribers.add(sub); }
public void unsubscribe(ScoreSubscriber sub) { subscribers.remove(sub); }
public synchronized void recordBall(int runsScored, boolean isWicket, String commentary) {
this.runs += runsScored;
if (isWicket) this.wickets++;
this.totalBalls++;
double overs = (totalBalls / 6) + (totalBalls % 6) / 10.0;
double crr = crrStrategy.calculate(runs, totalBalls);
String enrichedCommentary = commentary + String.format(" [CRR: %.2f]", crr);
for (ScoreSubscriber sub : subscribers) {
sub.onScoreUpdated(runs, wickets, overs, enrichedCommentary);
}
}
}
public class Main {
public static void main(String[] args) {
MatchScoreboard scoreboard = new MatchScoreboard();
scoreboard.subscribe(new MobileAppSubscriber("User_101"));
scoreboard.subscribe(new MobileAppSubscriber("User_202"));
System.out.println("--- Starting Live Match Simulation ---");
scoreboard.recordBall(4, false, "FOUR! Driven through extra cover.");
scoreboard.recordBall(0, true, "OUT! Clean bowled through the gate!");
scoreboard.recordBall(6, false, "SIX! Massive strike over long-on!");
}
}