[FIXED] How to restrict access privilege between two classes in java?

Issue

I have two classes Player and Game

public class Player {
    private List<String> cards;

    public Player(){
        cards = new ArrayList<String>();
    }
}
public class Game {
    List<Player> players;

    public Game(List<Player> players){
        this.players = new ArrayList<Player>();

        for(Player player: players){
            this.players.add(player);
        }

    }

    private void distributeCards(){
        ...

    }

    public void start(){
        distributeCards()
        ...

    }

}

the distributeCards method of Game class needs to modify the cards attribute of a Player object. If I declare a public or protected method in the Player class for that, other classes in the same package will have the privilege as well but I don’t want that. In this situation what can I do?

Let me know if I’m breaking any rules of design principles or design patterns.

Solution

Things I would consider, given the assumption that the Player must expose the list of cards somehow:

  • Maybe, in your design & implementation, the cards are not a property of the player but of the game; e.g.:

      public class Game {
          List<Player> players;
          Map<Player, List<String>> cards;
    

    This would work if only the Game wants to know the list of cards or if the rest of the system can ask the Game for the list of cards of a specific Player.

  • Maybe the Game is responsible for providing the Player objects, so it has access to its internal state. Player could be an interface that Game is responsible for providing with a private inner class. Or Player is an immutable class, constructed by the Game with a list of cards.

    Another way to approach this option is to think e.g. that a "Player" is actually a user within a game. So the Game takes the list of users that will be playing and transforms it to a list of Player objects. The Game is responsible for the list of cards in the Player object.

  • A giveCard(Card) method just like Code-Apprentice writes. This means that you rethink your constraints and allow everyone to modify the Player. Additionally you can even make a convenience method giveCards(List<Card>) where the Player copies the card list argument in its internal state. This way no outsider is able to directly modify the Player‘s internal state.

    As an amendment to this option, you could add validation logic to the giveCards(List<Card>) method, so that e.g. it throws an IllegalStateException if the Player already has cards. The drawback here is that this behavior is not obvious from the design of the class.

  • You could move the logic of card distribution to the Player. So, e.g. the Game shuffles the cards and gives the shuffled list to the first Player; the Player picks some cards, moves them to its internal state and removes them from the shuffled list; Game gives the remaining cards to the next Player. This is not always appropriate, you judge according to your design.

  • In my own experiments with card games the design was that Game, Player, Card, Hand (the list of cards of a Player) etc are immutable value objects holding the current state of a game. (Hint: check out Immutables.) Another class, say GameEngine, is responsible for taking a state and returning a new immutable state in response to a user or game action. Game is the root state object, everything else is reachable through it. E.g.:

      public class GameEngine {
          public Game distributeCards(Game game) {
              ...
          }
      }
    

    You can add the business logic methods to the Game class if you want, the gist here is the immutability and that each action returns a new state. Think Redux for Java 🙂

Answered By – Nikos Paraskevopoulos

Answer Checked By – David Goodson (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published