Building Robust Java Applications with SOLID Principles: A Sports Team Analogy
Tags: java, solidprinciples, programming, coding
Published: 2024-04-30 16:45:11

Have you ever thought that your code could be more robust overall, more readable, or more flexible? If so, you’re at the right place at the right time. We’ll look at the SOLID principles of object-oriented design, a collection of rules that can make your code more than just functional — it can make it truly elegant.

To make this journey more engaging, let’s dive into a unique analogy involving the management of a sports team roster.

## 1. Single Responsibility Principle (SRP): HeadCoach

Imagine a basketball team with a head coach who has a clear, singular responsibility — to manage the team’s strategy and training sessions. This aligns perfectly with the Single Responsibility Principle, where a class should have only one reason to change.

```
class HeadCoach {
    public void developWinningStrategy() {
        // write you logic for developing game strategy here
    }

    public void conductTeamTraining() {
        // write you logic for conducting team training here
    }
}
```
In this example, the HeadCoach class do encapsulates all responsibilities related to team strategy and training, adhering to SRP.

## 2. Open/Closed Principle (OCP): Player Positions

The OCP in cricket refers to players who can adapt to different roles and situations. In Java, this principle emphasizes that classes should be open for extension but closed for modification. Just as an all-rounder in cricket can bat, bowl, and field competently, a well-designed Java class should allow for new functionalities to be added through inheritance or interfaces without altering existing code.

```
interface Cricketer {
    void play();
}

class Batsman implements Cricketer {
    @Override
    public void play() {
        System.out.println("Batsman is scoring runs.");
    }
}

class Bowler implements Cricketer {
    @Override
    public void play() {
        System.out.println("Bowler is delivering the ball.");
    }
}

public class CricketTeam {
    public static void main(String[] args) {
        Cricketer batsman = new Batsman();
        Cricketer bowler = new Bowler();

        batsman.play();
        bowler.play();
    }
}
```
The Open/Closed Principle encourages us to design flexible and extensible systems by allowing new functionality (or player positions) to be added without altering existing code.

## 3. Liskov Substitution Principle (LSP): Substitutable Players

In our analogy, different players should be substitutable without affecting the game’s outcome. This mirrors the Liskov Substitution Principle, ensuring that subclasses can replace their parent class instances without altering the program’s behaviour.

```
class Player {
    void performRole() {
        // Common logic 
    }
}

class SubstitutePlayer extends Player {
    // implementation for substitute player
}

// best practice: SubstitutePlayer can replace Player without issues

```

## 4. Interface Segregation Principle (ISP) — The Specialized Role Player

This principle suggests that clients should not be forced to depend on interfaces they do not use. In cricket, not every player needs to be an expert in all facets of the game. Likewise, Java interfaces should be focused and specific to the needs of the implementing classes, preventing unnecessary dependencies.

```
interface Batsman {
    void hitSix();
}

interface Bowler {
    void bowl();
}

class AllRounder implements Batsman, Bowler {
    @Override
    public void hitSix() {
        // Logic to hit a six
    }

    @Override
    public void bowl() {
        // Logic to bowl a delivery
    }
}

// Best practice: Interfaces are specific to roles to prevent unnecessary dependencies
```

## 5. Dependency Inversion Principle (DIP) — The Team Strategy

Just as a cricket team’s success relies on a cohesive game plan rather than individual brilliance, Java classes should rely on interfaces or abstract classes to promote loose coupling and easier testing.

```
interface TeamStrategy {
    void formulateStrategy();
}

class CricketTeam implements TeamStrategy {
    @Override
    public void formulateStrategy() {
        // Implement team strategy
    }
}

class Match {
    private final TeamStrategy teamStrategy;

    public Match(TeamStrategy teamStrategy) {
        this.teamStrategy = teamStrategy;
    }

    public void executeMatch() {
        teamStrategy.formulateStrategy();
        // Execute the match based on the strategy
    }
}

// best practice: Match class depends on abstraction (TeamStrategy) rather than concrete implementation (CricketTeam)
```

## Conclusion

Through the use of the cricket team analogy, developers can apply the SOLID principles to Java development and create applications that are more flexible, resilient, and easier to maintain. With the help of this analogy, developers can more effectively visualize and apply SOLID principles, creating codebases that are cleaner, and more scalable.

## References
https://www.baeldung.com/solid-principles

https://www.jrebel.com/blog/solid-principles-in-java?source=post_page-----a99aa195907d--------------------------------

https://blogs.oracle.com/javamagazine/post/curly-braces-java-solid-design

**Github: https://github.com/tharindu1998/solid-principles**