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.
Things I would consider, given the assumption that the
Playermust 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.:
This would work if only the
Gamewants to know the list of cards or if the rest of the system can ask theGamefor the list of cards of a specificPlayer.Maybe the
Gameis responsible for providing thePlayerobjects, so it has access to its internal state.Playercould be an interface thatGameis responsible for providing with a private inner class. OrPlayeris an immutable class, constructed by theGamewith 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
Gametakes the list of users that will be playing and transforms it to a list ofPlayerobjects. TheGameis responsible for the list of cards in thePlayerobject.A
giveCard(Card)method just like Code-Apprentice writes. This means that you rethink your constraints and allow everyone to modify thePlayer. Additionally you can even make a convenience methodgiveCards(List<Card>)where thePlayercopies the card list argument in its internal state. This way no outsider is able to directly modify thePlayer'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 anIllegalStateExceptionif thePlayeralready 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. theGameshuffles the cards and gives the shuffled list to the firstPlayer; thePlayerpicks some cards, moves them to its internal state and removes them from the shuffled list;Gamegives the remaining cards to the nextPlayer. 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 aPlayer) etc are immutable value objects holding the current state of a game. (Hint: check out Immutables.) Another class, sayGameEngine, is responsible for taking a state and returning a new immutable state in response to a user or game action.Gameis the root state object, everything else is reachable through it. E.g.:You can add the business logic methods to the
Gameclass if you want, the gist here is the immutability and that each action returns a new state. Think Redux for Java :)