Questions and Answers

Q: Why does the Game class run the sequence of steps? Isn't that the responsibility of some main program?
Q: Why is Outcome a separate class? Each object that is an instance of Outcome only has two attributes; why not use an array of Strings for the names, and a parallel array of integers for the odds?
Q: If Outcome encapsulates the function to compute the amount won, isn't it just a glorified subroutine?
Q: What is the distinction between an Outcome and a Bet?
Q: Why are the classes so small?
Q:

Why does the Game class run the sequence of steps? Isn't that the responsibility of some “main program?

A:

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.

Q:

Why is Outcome a separate class? Each object that is an instance of Outcome only has two attributes; why not use an array of Strings for the names, and a parallel array of integers for the odds?

A:

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.

Q:

If Outcome encapsulates the function to compute the amount won, isn't it just a glorified subroutine?

A:

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.

Q:

What is the distinction between an Outcome and a Bet?

A:

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.

Q:

Why are the classes so small?

A:

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.