A Seat
should know whether or not it is occupied. Simply because someone joins or leaves the game doesn't mean you need a new Seat()
. The player has just vacated that spot at the table.
That being said, the PlayerId
and Money
properties are currently value types, meaning they will always have a value. In this case it appears they do not always have a value, so the first change should be to make those properties nullable (the second change I would make is to use decimal
as the data type for the Money
property).
Currently everything in both Table
and Seat
is public. In the very least you want to enforce some encapsulation, and make the setters for your properties private. My first sentence has some bold face words and phrases, which you can use to guide your design of the Seat
class:
- is occupied
- joins or leaves the game
- vacated
These give you some of the basic operations that can be performed on a Seat
. The Seat
class should have a property that returns whether or not this particular seat is currently occupied. When both PlayerId
and Money
are null, then seat.IsOccupied
should return false.
This leads us to discover our first class invariant: when PlayerId
is not null, then Money
should not be null as well. The setters for both properties need to be private. Something needs to ensure when a Seat
becomes occupied that both player and money have values. We need a public method on the Seat class called Occupy
that takes both values: public void Occupy(int playerId, decimal money)
. Here we can use the Type System in C# to ensure both values are not null by virtue of the fact they are both value types.
Now when you create a new Seat()
the player Id and money properties are both null
and seat.IsOccupied
returns false. After calling seat.Occupy(45, 1200.00m)
both properties are set, and then seat.IsOccupied
returns true.
Finally, when someone leaves the game, they vacate the seat. The seat.Vacate()
method should set both PlayerId
and Money
to null, which will cause seat.IsOccupied
to return false.
var seat = new Seat(); // Create vacant seat
seat.Occupy(45, 50.00m);
// Play game
seat.Vacate(); // Spouse calls. Kids are going crazy. Time to go home.
But we still have a problem. Calling seat.Vacate()
makes the player (and their money) disappear. While this works out great for players that owe the House money (or maybe it doesn't...) players that still have money do not want to blink out of existence simply because they left the game. You need a Player
class to encapsulate the player Id and money properties. So a Seat
really only needs a Player
in order to determine if it IsOccupied
:
var player = new Player(45, 50.00m);
var blackJackTable = new Table();
var texasHoldemTable = new Table();
blackJackTable.Seats[0].Occupy(player);
Console.Writeline(blackJackTable.Seats[0].IsOccupied); // Prints "True"
blackJackTable.Seats[0].Vacate();
texasHoldemTable.Seats[2].Occupy(player);
texasHoldemTable.Seats[2].Vacate(); // Spouse calls. Kids are going crazy. Time to go home.
Now a Player
can occupy and vacate a seat, and then transfer their winnings to another game.
But we still have a problem. You need to know which seat is available when joining a table. The Table
class needs some encapsulation as well. The Table
class should expose a public method allowing you to join the table. In fact, public Seat Join(Player newPlayer)
would be a great method to add to the Table class. That encapsulates logic for joining the table. It returns a Seat
which at a later time the player can choose to Vacate()
. By exposing a public boolean property on Table
called IsFull
that returns true if all Seats
are occupied, code outside the Table
class does not need access to the Seats array. The Seats array should be completely hidden. Make Seats
a private, read-only property. This is called information hiding, and is another fundamental concept of object oriented programming.