Greg Searle's design
(New page: There are many things that must be known before looking at my design indepth. First it would be logical to understand the game of blackjack so Greg Searle's project blackjack has a ni...) |
m (Greg Searle's Design moved to Greg Searle's design: lowercased name) |
Revision as of 05:39, 29 July 2008
There are many things that must be known before looking at my design indepth. First it would be logical to understand the game of blackjack so Greg Searle's project blackjack has a nice introduction.
Here is my current design:
Contents |
Design Decisions
There are several times when I have looked at my code and said "should I...". Here are some of the decisions I made:
- I chose to add the Box class. I was having trouble trying to figure out how to clear the table of all of the cards at the end or a round. I couldnt ask the players because each player may be playing more than one hand. I couldnt ask the hand because if a hand was split then I would be asking it twice. This was where I realised that I was missing a link. The box class is that link.
- The Table class. I didn't like the fact that my code was controlled by the Dealer. The table class was to be the controller. Also I was planning on implementing different rules for different games and the table class seemed like a good place to be able to create and control these rules. Blair Neate and Alex Wong both heard the name Controller and said God class. I say no, using Tell, don't ask and having a God class is beyond my comprehension atm.
- PlayingStrategy came about because this game is meant to be a statistical generator for different ways of playing the game. This was an easy choice. This uses a Strategy pattern. I was having a little trouble deciding if this was a Strategy pattern or a State pattern. I have called it a strategy because it is simply an algorithm, its not meant to change between other states; its set. State however had the multiple methods going for it, strategy in the GoF book only has one method, whereas state has a few. I suppose in theory I could have multiple strategies depending upon what I am asking the strategy to decide, but thats a waste of space. Ill Do the simplest thing that could possibly work and if for any reason in the future i need multiple ones, I can always change it. (Thinking about if a GUI end was put on this and the person wanted to choose to have this component for this choice, but take that for another.)
- As mentioned in Greg Searle's captain's log star date (somewhere in the mess) I had a Command pattern. This was actually generated by accident. This was removed because it was overkill for what I was using it for. The command was a decision about what the player would do. This, although looking nice in my eyes, was overkill because what the Command pattern is primarily used for is generating a history of what has happened. My program wouldn't care if the tool split his tens (If you ever do this, analyse why you are at the casino. 20 is a winning hand.).
- Aces. After writing this assignment, I hate them. Well maybe its not them, but "blackjacks". I wish this game never had them ;). An ace can be either a 1 or an 11. Wal proposed to me that they be stored as a 1 in the cards and handled in the Hand. The biggest question here is: what value does an ace have outside of a hand? I chose to agree with Wal and make the hand handle the aces. There is a boolean to keep track of if there is an ace in the hand. Thought about a polymorphism type for ace, really didnt want to, and Wal agreed that it wasnt a good idea.
- At one time during my design I had a heirachy between Player and Dealer. The larger these classes got, the less and less they had similar behavior. I removed this heirachy. The reason they were initally subclassed was mainly to aid the Hand class which, using Tell, don't ask, has to know it's owner. So as a hand had behavior similar for both a Player and a Dealer I made a heirachy. However this became much more of a problem when more functionality was added so I removed it. Following Favour composition over inheritance I note that a Dealer isn't really (imho) a participant of the game (Parent class called Parent), but a part of the game. What I did to capture the loss was to differentiate the difference between a PlayersHand and a DealersHand. There are some blatantly obvious choices for doing this:
- A Ddealer can't split their hand.
- A Player places a bet on their Hand
- A Player can double their bet on their hand.
- A PlayersHand has a box
- As you can see: a difference.
But they are not completely different. Each hand has a value that can be compared to each other. It can accept another card. Although a Dealer can only have a DealersHand and a Player can only have a PlayersHand. This is because of the Liskov substitution principle and also Once and only once (The reason for the heirachy).
- I have a betting strategy to decide how much money the player wants to bet on the upcoming hand. The choice for this was quite a tricky one. I could have had the Player have two different strategies: Playing and Betting. However because the goal of my project was to figure out the best way to win in Blackjack I was wanting to be able to compare my Strategies with the best: Card Counting. Card counting varies the bet with respect to how it is playing. They would be tightly coupled through the player if this was not the case. For instance, the playing strategy has worked out that it stands a very small chance to win on the next hand so it bets low. All information about what to do next is stored in the strategy. I see this as the Players brain (although if you go to the casino you may find that not all players have one). The brain stores information and determines what to do with it once it has it. For a Card counting simulation the playing strategy must keep track of all the cards that have previously been. It is logical to have the data about making the decision stored in the same class if we Keep behaviour with data.
- I was intending on having easily interchangable rules. For instance: In NZ we play with the same rules as Aussies, apart from the fact that they shuffle after every hand. In America they can double on soft hands, we cant. This is a major refactor and I don't have the time.
- I keep looking at Hand and wondering if it needs a State pattern. It is starting to get more and more states. There are all these different things that determine what the hand can do. Such as if the DealersHand isBlackjack then the players hand can be offered insurance, or offered even money. If the PlayersHand contains two Cards (and only two Cards) that have the same value then they can split (isSplittable). If the hand has only two cards and neither is an Ace then is can be doubled (canDouble). I have avoided using the State pattern in order to get a working system on time, and I would still need to see if it would really help.
- Could have used a Flyweight pattern to represent my Cards and the Shoe, but overkill I think. I would have to conduct some profiles of my code before even trying this one.
- I use the Observer pattern in my code. The dealer gets notified if the ShuffleCard has been dealt and the PlayersPlayingStrategy gets notified every time a card is dealt.
- I will use the cheap: I use the Iterator pattern in my code. I'd be really worried if I didn't.
- I use the Strategy pattern in many cases as talked about above.
* I have used a Singleton pattern for the ShuffleCard.
Heuristics Followed
- After I saw Blair Neate had managed to successfully apply Goto considered harmful on his ~60 classes, I refactored my code to do the same.
- In a strange sort of way I have obeyed keep behaviour with data. By using [[Tell, don't ask I keep passing parameters along until somethings that needs to know about it uses it.
- I have avoided [[Comments smell]. However alot of the terminology used is with respect to the domain. I hope the introduction page helps.
- I have managed to Avoid concrete base classes. But that's obvious with such a small project.
- Beware accessors - I'm using Tell, don't ask almost everywhere. I have barely any getters, a few more setters (but they don't have names setX() does that count ;))
- Beware singletons I have only one Singleton pattern, my ShuffleCard.
TellDontAsk
In my design I have tried to use TellDontAsk as a fundamental heuristic. I dont know if this is/was the best option, but I the more I used it the more I liked it. What I find it brings to a project is flow. Tracking errors seems to be a logical step process.
I have a view in my head of TellDontAsk as a pipeline. The dealer is the start and information flows toward the player through the table, through the box, through the hand and into the player gathering/contributing information when required.
I have not used this throughout my design, as there are times when even I dont think that its needed. An example of this is the Shoe class. All it needs to do is be able to return a card, so I do that.
TellDontAsk requires alot of bi-directional links. So every hand has a player that it belongs to and a box that it is in etc.
Things to note
While looking through my code to see if I had any refactorings that stood out, I noticed a nice switch statement in my code. This code was in the compareTo methods throughout my code. I thought, hmmm, a switch statement; time for a refactor. What a crock. Where would I put it? I figure that if I KeepBehaviourWithData then I'm doing alright on this one. (Same goes for toString and algorithm methods (such as in my strategies))
My code is on the brink of bursting into somethings that is really aesthetic. Getting the Rule thing to go would be a classic example of a real world situation which would require me to pull information from the big names such as GoF, Riel, Fowler etc. I'm really starting to get into this design, would love a few more weeks (But this project will never be complete).
This was an implementation more than a refactor. The inital code I started from was only a skeleton, I had some conflicts with most of what it did so I wouldn't say there is a single piece of it left in the code. Our conflicts came about because it was too limited. It only allowed one hand for one player per game. The deck (Shoe of sorts) was only 52 cards.