The first section, Preliminary Survey of Classes, is a survey of the classes gleaned from the general problem statement. Refer to Problem Statement as well as the problem details in Roulette Details. This survey is drawn from a quick overview of the key nouns in these sections.
We’ll amplify this survery with some details of the class definitions in Preliminary Roulette Class Structure.
Given this preliminary of the candidate classes, A Walkthrough of Roulette is a walkthrough of the possible design that will refine the definitions, and give us some assurance that we have a reasonable architecture. We will make some changes to the preliminary class list, revising and expanding on our survey.
We will also include a number of questions and answers about this preliminary design information. This should help clarify the design presentation and set the stage for the various development exercises in the chapters that follow.
To provide a starting point for the development effort, we have to identify the objects and define their responsibilities. The central principle behind the allocation of responsibility is encapsulation; we do this by attempting to isolate the information or isolate the processing that must be done. Encapsulation assures that the methods of a class are the exclusive users of the fields of that class. It also makes each class very loosely coupled with other classes; this permits change without a ripple through the application. For example, each Outcome contains both the name and the payout odds. That way each Outcome can be used to compute a winning amount, and no other element of the simulation needs to share the odds information or the payout calculation.
In a few cases, we have looked forward to anticipate some future considerations. One such consideration is the house rake, also known as the vigorish, vig, or commission. In some games, the house makes a 5% deduction from some payouts. This complexity is best isolated in the Outcome class. Roulette doesn’t have any need for a rake, since the presence of the 0 and 00 on the wheel gives the house a little over 5% edge on each bet. We’ll design our class so that this can be added later when we implement Craps.
In reading the background information and the problem statement, we noticed a number of nouns that seemed to be important parts of the game we are simulating.
One common development milestone is to be able to develop a class model in the Unified Modeling Language (UML) to describe the relationships among the various nouns in the problem statement. Building (and interpreting) this model takes some experience with OO programming. In this first part, we’ll avoid doing extensive modeling. Instead we’ll simply identify some basic design principles. We’ll focus in on the most important of these nouns and describe the kinds of classes that you will build.
We’ll summarize some of the classes and responsibilities that we can identify from the problem statement. This is not the complete list of classes we need to build. As we work through the exercises, we’ll discover additional classes and rework some of these preliminary classes more than once.
We’ll describe each class with respect to the responsibility allocated to the class and the collaborators. Some collabotors are used by an object to get work done. We have a number of “uses-used by” collaborative relationships among our various classes.
A name for the bet and the payout odds. This isolates the calculation of the payout amount. Example: “Red”, “1:1”.
Selects the Outcome s that win. This isolates the use of a random number generator to select Outcomes; and it encapsulates the set of winning Outcomes that are associated with each individual number on the wheel. Example: the “1” bin has the following winning Outcomes: “1”, “Red”, “Odd”, “Low”, “Column 1”, “Dozen 1-12”, “Split 1-2”, “Split 1-4”, “Street 1-2-3”, “Corner 1-2-4-5”, “Five Bet”, “Line 1-2-3-4-5-6”, “00-0-1-2-3”, “Dozen 1”, “Low” and “Column 1”.
A collection of bets placed on Outcomes by a Player. This isolates the set of possible bets and the management of the amounts currently at risk on each bet. This also serves as the interface between the Player and the other elements of the game.
Places bets on Outcomes, updates the stake with amounts won and lost.
The class Player has the most important responsibility in the application, since we expect to update the algorithms this class uses to place different kinds of bets. Clearly, we need to cleanly encapsulate the Player, so that changes to this class have no ripple effect in other classes of the application.
A good preliminary task is to review these responsibilities to confirm that a complete cycle of play is possible. This will help provide some design details for each class. It will also provide some insight into classes that may be missing from this overview.
A good way to structure this task is to do a Class-Reponsibility-Collaborators (CRC) walkthrough.
As preparation, get some 5” x 8” notecards. On each card, write down the name of a class, the responsibilities and the collaborators. Leave plenty of room around the responsibilities and collaborators to write notes. We’ve only identified five classes, so far, but others always show up during the walkthrough.
During the walkthrough, we identify areas of responsibility, allocate them to classes of objects and define any collaborating objects. An area of responsibility is a thing to do, a piece of information, a result. Sometimes a big piece of responsibility can be broken down into smaller pieces, and those smaller pieces assigned to other classes. There are a lot of reasons for decomposing, the purpose of this book is to explore many of them in depth. Therefore, we won’t justify any of our suggestions until later in the book. For now, follow along closely to get a sense of where the exercises will be leading.
Our preliminary note was that this class “Runs the game.” The responsibilities section has a summary of four steps involved in running the game.
The first step is “gets bets from :class:Player`.” Find the Player card.
One of the responsibilities of a Player is to place bets. The step in the responsibility statement is merely “Places bets on :class:”Outcome`s.” Looking at the classes, we note that the Table contains the amounts placed on the Bets. Fix the collaboration information on the Player to name the Table class. Find the Table card.
What class contains the individual “amount bet on an Outcome?” This class appears to be missing. We’ll call this new class Bet and start a new card. We know one responsibility is to hold the amount bet on a particular Outcome.
We know three collaborators: the amount is paired with an Outcome, all of the Bet s are collected by a Table, and the Bet s are created by a Player. We’ll update all of the existing cards to name their collaboration with Bet.
You should continue this tour, working your way through spinning the Wheel to get a list of winning Outcomes. From there, the Game can get all of the Bets from the Table and see which are based on winning Outcomes and which are based on losing Outcomes. The Game can notify the Player of each losing Bet, and notify the Player of each winning Bet, using the Outcome to compute the winning amount.
This walkthrough will give you an overview of some of the interactions among the objects in the working application. You may uncover additional design ideas from this walkthrough. The most important outcome of the walkthrough is a clear sense of the responsibilities and the collaborations required to create the necessary application behavior.
Why does the Game class run the sequence of steps? Isn’t that the responsibility of some “main program?”
Coffee Shop Answer. We haven’t finished designing the entire application, so we need to reflect our own ignorance of how the final application will be assembled from the various parts. Rather than allocate too many responsibilities to Game, and possibly finding conflicts or complication, we’d rather allocate too few responsibilities until we know more.
From another point of view, designing the main program is premature because we haven’t finished designing the entire application. We anticipate a Game object being invoked from some statistical data gathering object to run one game. The data gathering object will then get the final stake from the player and record this. Game ‘s responsibilities are focused on playing the game itself. We’ll need to add a responsibility to Game to collaborate with the data gathering class to run a number of games as a “session”.
Deeper Answer. In procedural programming (especially in languages like COBOL), the “main program” is allocated almost all of the responsibilities. These procedural main programs usually contain a number of elements, all of which are very tightly coupled. We have seen highly skilled programmers who are able to limit the amount of work done in the main program, even in procedural languages. In OO languages, it becomes possible for even moderately skilled programmers to reduce the main program to a short list of object constructors, with the real work delegated to the objects. We find that “main program” classes are relatively hard to reuse, and prefer to keep them as short as possible.
Representation. We prefer not to decompose an object into separate data elements. If we do decompose this object, we will have to ask which class would own these two arrays? If Wheel keeps these, then Table becomes very tightly coupled to these two arrays that should be Wheel ‘s responsibility. If Table keeps these, then Wheel is priviledged to know details of how Table is implemented. If we need to change these arrays to another storage structure, two classes would change instead of one.
Having the name and odds in a single Outcome object allows us to change the representation of an Outcome. For example, we might replace the String as the identification of the outcome, with a collection of the individual numbers that comprise this outcome. This would identify a straight bet by the single winning number; an even money bet would be identified by an array of the 18 winning numbers.
Responsibility. he principle of isolating responsibility would be broken by this “two parallel arrays” design because now the Game class would need to know how to compute odds. In more complex games, there would be the added complication of figuring the rake. Consider a game where the Player‘s strategy depends on the potential payout. Now the Game and the Player both have copies of the algorithm for computing the payout. A change to one must be paired with a change to the other.
The alternative we have chosen is to encapsulate the payout algorithm along with the relevant data items in a single bundle.
If Outcome encapsulates the function to compute the amount won, isn’t it just a glorified subroutine?
In a limited way, yes. A class can be thought of as a glorified subroutine library that captures and isolates data elements along with their associated functions. For some new designers, this is a helpful summary of the basic principle of encapsulation. Inheritance and subclasses, however, make a class more powerful than a simple subroutine library with private data. Inheritance is a way to create a family of closely-related subroutine libraries in a simple way that is validated by the compiler.
We need to describe the propositions on the table on which you can place bets. The propositions are distinct from an actual amount of money wagered on a proposition. There are a lot of terms to choose from, including bet, wager, proposition, place, location, or outcome. We opted for using Outcome because it seemed to express the open-ended nature of a potential outcome, different from an amount bet on a potential outcome. In a way, we’re considering the Outcome as an abstract possibility, and the Bet as a concrete action taken by a player.
Also, as we expand this simulation to cover other games, we will find that the randomized outcome is not something we can directly bet on. In Roulette, however, all outcomes are something we can be bet on, as well as a great many combinations of outcomes. We will revisit this design decision as we move on to other games.
Why are the classes so small?
First-time designers of OO applications are sometimes uncomfortable with the notion of emergent behavior. In procedural programming languages, the application’s features are always embodied in a few key procedures. Sometimes a single procedure, named main.
A good OO design partitions responsibility. In many cases, this subdivision of the application’s features means that the overall behavior is not captured in one central place. Rather, it emerges from the interactions of a number of objects.
We have found that smaller elements, with very finely divided responsibilities, are more flexible and permit change. If a change will only alter a portion of a large class, it can make that portion incompatible with other portions of the same class. A symptom of this is a bewildering nest of if-statements to sort out the various alternatives. When the design is decomposed down more finely, a change can be more easily isolated to a single class. A much simpler sequence of if-statements can be focused on selecting the proper class, which can then simply carry out the desired functions.