Let’s build a deck of cards today using a hybrid approach, that is to say, lets use good Object Oriented design but keep our objects stateless and our functions idempotent, a la Gary Bernhardt style, Faux-O, or “Functional Core/Imperative Shell”.
I am going to skip the tests for brevity - the test ‘noise’ in non-testing articles is a bit much for this format. I however do have tests, and I encourage you to write tests!
We are going to need to describe a deck of cards using objects, so let’s look for some logical boundaries and give them names. One way we can do this by describing what we want in a paragraph and a title. Let’s do that now!
After I wrote that paragraph I underlined what I would call actors, which may or may not become objects or collections of objects. These are usually the recurring nouns.
Now that we have some sense of what we need to build, let’s get into it!
We need an initializer that accepts a “shoe size”, and the initializer needs to set our local variable @cards to the results of
build_deck, which will be an array - that way we can multiply it against our shoe size.
We now have a class to describe a “deck” of “cards”. We can create one with
Deck.new and we can specify the “shoe size” or not when we create one.
There is a mention there of
build_deck which is as of yet, undefined.
I want to keep this simple and stateless, so Deck will have only one to have one public method, the initializer
.new. Every other method will be protected, and hidden from the public API, which means we can change the behavior of this class without breaking compatibility!
build_deck method will simply return cards representing all suits and all ranks. But we do not have any suits or ranks or cards defined yet, so let’s do that now.
The Rank object is a simple struct, which I love for value objects, as is the Card.
We can now build a Card object which is composed of a Rank object, and a suit, which is just a string. The
to_s methods simply tells the class how to respond with it’s own description in English, ie:
We will now need to store the rank and suit strings, so let’s put them in our Deck object.
build_deck will also need access to an
all_ranks method so we can then iterate and produce cards representing those ranks. Let’s implement that one next.
Now that we have these objects to work with, let’s implement our
build_deck method! This method will be composed of a couple of
flat_maps to join together the correct number of each rank and suit to build a standard deck.
all_ranks returns us an array of _Rank_s representing all the ranks in a deck of cards. We now use that array to map a card representing each “suit”. This ends up giving us 52 _Card_s representing the standard deck.
All together now!
Now we can build a deck of cards an access the cards!
Now you may be saying, what about being able do DO anything with these cards? I will ask you now, “Does a Deck shuffle and deal itself?”
Yeah, not unless you are on drugs or Disney. Your objects should also not get all Fantasia here.
Build a DeckShuffler object to shuffle a Deck, build a BlackjackDealer object to deal, and a BlackjackScorer object to score the rounds. Are you seeing a pattern here? :)
Till next time, keep that shit OO and functional my friends.