javadesign-patternsstate-pattern

State Design Pattern Usage In Tennis Kata


I am solving the famous Tennis Kata Problem

I have a basic question around state design pattern. The programme needs to output the score for a tennis game.

Below enum represents the different scores a player in tennis game can have :

public enum Score{
Love,
Fifteen,
Thirty,
Fourty,
Deuce,
Advantage,
Game
}

Typical classes I am planning to use here are : TennisGame (main class for handling logic), Player (will maintain its own Score), ScoreCalculator (have Score manipulation logic based on Rules) The scores will be changed when an external event happens which indicates which user has scored the point.

The question here is - how the logic to track score changes should be handled ?

Very naive approach will be :

if(score == Love){
  score = Fifteen; //move score to Fifteen 
}else if(score == Fifteen){
  score = Thirty; //move score to Thirty
}

But this will lead to a lot of if else conditions. I am thinking to use a state pattern for this.

But the question here is , the context here (e.g: TennisGame class) which will encapsulate State won't be doing anything based on State changes.

It is just returning the score once a point is scored by a player.

Can anyone please advice what should be the correct design using State pattern here ?


Solution

  • First of all, I don't think that you need the Deuce in your score enum, as it's a state when the score is 40-40. In this case, when you can have a situation when score is 30-40, and if player one scores then you have to change the state for both players. It will be hard to manage. Here is the enum that is better to use from my perspective:

    public enum Score {
        Love, Fifteen, Thirty, Forty, Advantage, Game
    }
    

    The second thing that I think is better is not to save the current player score, instead store the count of win balls for both players, and then you can calculate the score based on these numbers. It's easy to achieve with the following code:

    public Score calculateScore(int playerWinBalls, int opponentWinBalls) {
        if (playerWinBalls <= 3) {
            return Score.values()[playerWinBalls];
        }
        int winBallsDifference = playerWinBalls - opponentWinBalls;
        if (winBallsDifference <= 0) {
            return Score.Forty;
        }
        if (winBallsDifference == 1) {
            return Score.Advantage;
        }
        return Score.Game;
    }
    

    Now what is left is to have an API for increase win the ball for the given player, it could be some method like winBall(String playerName), having separate methods like winBallP1()/winBallP2() or maybe something else.