Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

A Business Intelligence Approach to Bowling

5.00/5 (2 votes)
8 Mar 2018MIT27 min read 9.1K   59  
On data, context, class-like architecture in Javascript, and ten-pin bowling.

Image 1

Introduction

Scoring a game of ten-pin bowling is a common programming challenge posed for students and job applicants alike.  Regardless of language or platform, the programming problem is typically addressed with the concept of the current frame.  The current frame from 1 to 10 is maintained in a variable, with rolled-ball values (i.e. the number of pins knocked down by rolling a ball) applied to it for scoring.  There are usually variables to track whether there is a “strike working” or “spare working”, meaning that rolled values are also applied to previous frames.  Essentially, this approach manages the current frame as the pertinent operational unit, and scoring is applied relative to it.

A professional with a business intelligence background may perceive a more granular operational unit however:  the roll.  From that perspective, the concept of a frame is not fundamental but rather something that is derived from a list of rolls.  The roll becomes the operational unit that organizes our work, while the frame is a derived informational unit. 

Throughout this article the term bowling is used to mean the ten-pin variant of the game.  For those unfamiliar with the game or its scoring rules, Wikipedia’s entry on the topic is instructive[1].  Following a brief description of business intelligence and what is meant by operational and informational contexts, this article presents a scoring application written in JavaScript as it might be designed by a data warehousing or business intelligence professional.  Attention is given to architectural concerns in JavaScript, including a class-like inheritance design and the application of a namespacing strategy.

A Business Intelligence Perspective

OLAP.com provides a useful definition of business intelligence:

Quote:

The term Business Intelligence (BI) refers to technologies, applications and practices for the collection, integration, analysis, and presentation of business information.  The purpose of Business Intelligence is to support better business decision making.

from OLAP.com, Business Intelligence[2]

As valuable as the first sentence is in that description, the second reflects the heart of business intelligence.  Rooted in the support of wise decision-making, business intelligence practitioners strive to provide timely and reliable information derived from the data an organization produces through its normal daily operations and record-keeping.

It is worth noting that the term business intelligence as a means of labelling this function started gaining prominence in private enterprise in the 1980’s and 1990’s[3].  In the public sphere, professionals in higher education formally organized the institutional research field even earlier, in the early 1960’s[4].  Though institutional research has its roots in the social sciences, and business intelligence has its own in information technology, the fields are true siblings, frequently seeking to accomplish the same goal:  to help decision-makers make wise decisions by leveraging an organization’s data.

Providing meaning to data

There are various well-subscribed methodologies for business intelligence, but regardless of a practitioner’s chosen approach, one simple fact presents an unavoidable crux:  data only have meaning in a context.  To illustrate, consider this datum:

42

 

What does it mean?  Is it good?  Should we take some action?  Without additional context, there is no meaningfulness to that number.  It is just 42.  With context though, we can start to appreciate meaning: 

  • 42… students in my child’s high school English class section.  Is that good?  Add the context “42-to-1 student-to-teacher ratio” and maybe we think it could be better.
     
  • 42… millions of dollars in sales revenue.  Maybe that is good? It seems good. Add more context: “last year we earned 47 million in revenue; also, our expenses topped 50 million this year”.  Maybe it isn’t good after all.
     
  • 42… percent of students graduate from my college in six years.  Is that… bad?  If you add the context “my college’s rival has a 56% graduation rate”, then it seems pretty bad.  But add this context:  “last year the number was 40%, and the year before it was 39%” and suddenly this year’s 42 is understood as a meaningful improvement.

Any piece of data only has meaning when understood in a specific context.  Regardless of one’s chosen BI methodology, one can appreciate two general categories of context that provide meaning to data:  operational and informational

Operational contexts

We think of operational data as a record of events that have occurred during a given business process.  The events worth logging vary based on the nature of the work in which an organization engages.  For example, a school would find operational events such as “a student has registered for a class” meaningful, especially for its areas that manage enrollment, revenue, and instruction.  An organization involved in retail sales would find events such as “a customer has used a credit card to buy a product through our online store” important to several of its operations, including inventory tracking, customer relations, and financial accounting.

Operational data are meaningful in their operational context.  Though that may seem an obvious statement, it is a point that business intelligence professionals find themselves explaining frequently.  The values that are logged in the record of an operational event are only meaningful in the context of that singular event, as the operational event is the defining context that gives meaning to the recorded values.

One way to demonstrate this is to appreciate the nature of the questions that may be answered by viewing an operational record, and those which cannot.  For the school, the question “When did student x register for class y?” is answerable by reviewing the relevant registration record and noting the value in its “Registration_Date” field.  The question doesn’t just imply an operational context; it requires an operational context for a meaningful answer.

Now consider this question:  “Are our classes this year fuller or less full than they were last year?”  There is no concept of “yearly fullness” recorded in a single registration event.  Inspecting the “Registration_Date” field or any other in a registration record does nothing to answer this question in a meaningful way.  The data recorded in the operational context have meaning in that they describe the operational event.  They lack meaning for questions that go beyond that operational context.

Informational contexts

Beyond operational inquiries, decision-makers ask the kind of questions that, when answered meaningfully, help organizations with more strategic outcomes.  We might be asking “Are our classes fuller this year than last” for example because we may be deciding whether we should be hiring more instructors.  Questions like these contain concepts that have no meaning in an operational context, but are very meaningful to wise decision-making. 

Such informational questions often have layers of additional concepts embedded in them.  For example, defining what “full” means is a question potentially about either capacity or cost-effectiveness… or both.  The decision to associate this year to last implies that there is meaningful information to be gleaned in the comparison, with the comparison itself a concept requiring its own definition.  The idea of a two-year comparison naturally leads to the concept of a longer term trend, inviting even higher-level strategic questions like: “How many students can we expect over the next xxx years?  How should we expect that to affect our revenue over that time?  How should we plan for staffing accordingly?”

It is useful to think of these questions as existing in an informational context.  Though they cannot be answered by the inspection of a singular operational data record, the complete set of operational records in the aggregate still may be a useful source from which to derive information to help answer informational questions.

Deriving the Informational from the Operational 

The key to drawing useful information from operational data boils down to definition and interpretation.  We take an informational question, or collection of questions, and break them down into component definitions.  We might apply terms to these components like metrics or dimensions or information elements.  Whatever the term used (the term often being a function of a chosen methodology), these all refer to the granular units of information that would be meaningful to help address strategic questions.

Let’s return to the question for our example school:  “Are our classes this year fuller or less full than they were last year?”  Here is a list of granular informational elements and their descriptions that might begin to support a meaningful answer:

Course

A template for classes that may be offered at a given time.

Course Catalog

The complete collection of Courses.

Term

A defined duration of time in which students may register for, participate in, and complete instances of one or more Courses.

Class

An instance of a Course that is offered in a specific Term.

Class Catalog

The complete collection of Class instances offered in a specific Term.

Class Enrollment

The distinct count of students enrolled for a single Class offered in a given Term.

Term Student Count

The distinct count of students enrolled in any Class offered in a given Term.  Students who are enrolled in more than one Class are counted only once.

Total Class Enrollment

The total count of students enrolled for all Classes offered in a given Term.  Students who are enrolled in more than one Class are counted once for each Class enrolled.

Current Year – Fall Enrollment

The Total Class Enrollment value for the Fall term of the current year.

Previous Year – Fall Enrollment

The Total Class Enrollment value for the Fall term of the previous year.

 

At this point, one might think the comparison of Current Year – Fall Enrollment with Previous Year – Fall Enrollment would satisfy the informational question, especially if our strategic interest lay in increasing overall enrollment.  A different interpretation of the question may lead to these additional informational elements:

Class Capacity

The maximum number of students that may enroll for a given Class instance.

Class Enrollment Percentage

The value of Class Enrollment divided by Class Capacity for a given Class instance.

Full Class Threshold

The percentage value of .95.  We have decided that 95% enrollment in a class means it is “full”.

Class Count

The total number of distinct Class instances offered in a Term.

Full Class Count

The number of distinct Class instances where the Full Class Threshold is met (i.e. class enrollment is at 95% of capacity).

Term Class Fullness

The value of Full Class Count divided by Class Count for a given Term.

 

With these elements, we might address the question by comparing the Term Class Fullness values from each year.  This might be a good interpretation of the question if our strategic interest had more to do with managing class size as a function of cost-effectiveness.

It is useful to note that in detailing our informational needs (our strategic questions) as a list of granular informational elements, each element is expressed in its informational context.  None of the titles and descriptions need involve any discussion of operational data or underlying business operations.  Eventually, however, we look to leverage our operational data and determine whether or not they support our informational elements.

Definitions and Interpretation

Operational data support an informational element to the extent that one may reliably interpret the data to derive the element.  In cases where an informational element is not supported by operational data – cases where no reliable derivation can be made – we would say the operational data does not support the informational need.  That is a good signal to the organization to either adjust how operational data are being tracked so they can support the needed information, or decide that the information isn’t so important compared to other priorities.  Having strategic needs documented as granular elements helps expose these informational gaps. 

As we saw before, the element definition begins with a meaningful title and description understood in its informational context.  We add an operational interpretation to describe how to derive the element from operational data.  To illustrate, we will add an operational interpretation to a few earlier examples:

Element Title

Description

Operational Interpretation

Class Count

The total number of distinct Class instances offered in a term.

To derive this element, perform a COUNT DISTINCT on the records in the Class table where the field STATUS equals ‘A’ (meaning “Active”) and the field TERM equals the desired term code. 

Class Enrollment

The distinct count of students enrolled for a single Class in a Term.

To derive this element, perform a COUNT on the records in the Enrollments table where the field CLASS_ID is the identifier for the desired Class, the field TERM is the desired Term code, and the field ENRL_STATUS is the value ‘E’ (meaning “successfully enrolled”).

Class Capacity

The total number of students that may enroll for a given Class.

Class Capacity is recorded in the template Course record rather than an instance Class record.  For the desired class instance, identify its COURSE_ID value from its record in the Class table.  Then select the value for the ENROLLMENT_CAPACITY field in the Courses table record with the matching COURSE_ID. 

 

We create definitions for information elements, which include instructions on how to interpret operational data to derive the desired pieces of information for our strategic interests.  Based on those definitions, business intelligence practitioners seek to make the process of interpreting operational data as accurate, efficient and automated as possible, all toward making the delivery of that information reliable and convenient for its audiences.

Let’s Bowl

Bowling can be understood as having an operational context with operational data that is tracked throughout a game, from which scoring and display data for informational contexts are derived. 

The Operational context in bowling

There is a single operational event in the game of bowling for which we would track data.  The event is simply “a ball has been rolled” and the data tracked is the number of pins the roll has knocked down.  This operation – roll a ball – is repeated from as few as eleven times to as many as twenty-one in a single traditional ten-frame game.  This is not a large amount of data – up to twenty-one records, each with a single numeric field – but they are operational nonetheless as they represent the log of important events occurring during the game.

The order of the rolls is also a significant component to this data.  Because of the rules of bowling, the same rolled values in a different order may produce a different score.  The operational data of a bowling game therefore can be understood as a sequenced list of rolled values. 

Remembering that operational data have meaning only in an operational context, when we see this sequenced list of rolled values:

5, 4, 10, 6, 3, 7

we can answer the question “What value was my second roll? (four)”, or “How many rolls have I rolled thus far? (six)”.  Those are good operational questions.  What we cannot answer, at least not without further interpretation, is the question “What is my running score?”

Informational contexts in bowling

The question “What is my running score” is answerable in the first of two informational contexts.  The concept of frame also becomes meaningful at these informational stages, initially for scoring, and then for display. 

Scoring Frames.  The sequenced list of rolled values may be interpreted to derive a new list of objects that we’ll call scoring frames.  A single scoring frame is a data structure that holds up to three rolled values.  The rules of scoring in bowling are applied in this derivation to associate each roll to one or more scoring frames. 

For example, take the same list of rolls as our operational data:

5, 4, 10, 6, 3, 7

According to the scoring rules of bowling, the first two rolls form one complete scoring frame:  the 5 added to the 4 makes a running score of 9.  The next three rolls form the second scoring frame:  the first 10 is a strike – all the pins knocked down on the first roll – and a frame with a strike gets to include the next two rolls (6 and 3) in its score.  These same two rolls (6 and 3) are assigned also to a complete third scoring frame, leaving the 7 to form an incomplete fourth scoring frame.  Thus, from the original sequenced list of operational data we derive the following four scoring frames.  By associating rolled values to scoring frame slots this way, we take what was originally operational data and make it meaningful in an informational (scoring) context. 

Image 2

Display Frames.  We apply a second level of derivation to further interpret the scoring frames into a set of display instructions that we’ll call display frames.  The scoring logic has already been addressed.  Now we are addressing the conventions around how that scoring is reflected on a traditional scorecard. 

For example, if all the pins are knocked down at the beginning of a frame, the rolled value of 10 is not traditionally marked on the scorecard.  Instead, an “X” is marked in the second “boxed” slot of the frame.  This is simply the traditional convention.  It could be that in an alternate universe, scorecard marking in bowling evolved differently to just enter the value of 10 in the first display slot of the frame, but in ours, it’s an “X” in the box.

Taking the set of four scoring frames in our example above, we would derive the following ten display frames to reflect the current display state of the full scorecard:

Image 3

Though calculated appropriately by the scoring frame, the running score for the fourth display frame is left blank, respecting the convention that the running score is not marked for an incompletely scored frame.  Also a display convention, a third slot for marking rolled values is only applicable in the last frame of the game.

It may be strange to think that the scorecard-marking we traditionally apply in bowling is largely a matter of customs.  To the business intelligence perspective though, it is easy to see this as a second informational context derived from the first.  Each of these informational contexts provides something subtly different.  The scoring frame layer lets us know our proper total score at any moment of a game, which would be entirely suitable alone for accurate scoring.  The display frame layer gives us the instructions for the unique rendering of that scoring on a traditional scorecard.

Coding the Scoring Application

The accompanying code implements an approach to a scoring application for bowling inspired by a business intelligence perspective, in which meaningful scoring and display information is derived from operational data.  The code is written in JavaScript and organized using a class-like architecture in the following files:

MishaBowling.Core.js:  These are the core classes to manage the operational data -- the rolls in a bowling game -- and derive scoring and display information. 

Game

The Game class maintains the operational data for a bowling game, in the form of a list of sequential rolls.  It exposes methods for starting a new game, adding a roll, and processing the list of rolls to derive ScoringFrameList and DisplayFrameList objects.

ScoringFrameList

The ScoringFrameList class takes a list of rolls upon construction and generates a list of scoring frames.  This class does the work to interpret the list of rolls, determining which rolls should get assigned as scoring components for which specific frames.  Essentially, this is the class that implements the scoring logic of bowling.

ScoringFrame

The ScoringFrame class is a data structure that associates up to three roll values with a single sequential frame.  ScoringFrame objects are instantiated by a ScoringFrameList.

DisplayFrameList

The DisplayFrameList class takes a ScoringFrameList upon construction and generates a corresponding list of display frames from the scoring frames. 

DisplayFrame, DisplayFinalFrame

The DisplayFrame and DisplayFinalFrame classes do the work of interpreting an individual ScoringFrame, generating data to support its display.  These classes contain the logic to interpret, for example, that a 10 on a first roll in a normal scoring frame should be displayed as an "X" in the second (boxed) slot and a blank in the first (non-boxed) slot.  DisplayFinalFrame inherits from DisplayFrame and overrides the interpretation for the final frame of a game.

 

MishaBowling.Web.js:  These are classes for managing a game from a web-based client, with support for rendering display instructions as HTML code.

GameUI

The GameUI class maintains an internal Game object for conducting a bowling game, and exposes methods for user interaction and visual display of scores in an HTML web-based context.

HtmlDisplayFrameList

The HtmlDisplayFrameList takes a DisplayFrameList upon construction and generates an HTML string for output in a web page context.

HtmlDisplayFrame, HtmlDisplayFinalFrame

The HtmlDisplayFrame serves as a base class.  It represents an HTML-representation of a single "normal" frame, with the HTML retrieved through the use of the getHTML() method.  The HtmlDisplayFinalFrame class inherits from HtmlDisplayFrame, and overrides those elements that make the conversion of display instructions to HTML different for the final frame.

 

MishaBowling.Util.js:  Provides the utility class RandomRoller, for producing random roll values with an adjustable ability level. 

“Class-like architecture” in a prototype world

JavaScript is natively prototype-based, rather than class-based in its approach to objects.  The concept of a class as a native construct in other object-oriented languages like Java and C# does not exist in JavaScript.  A JavaScript object is little more than a collection of named values.  Even so, we can create a template from which to instantiate objects, and use the special prototype property to simulate class inheritance. 

A “class” to JavaScript starts with a function that is treated as an object constructor.  By convention, this function’s name is typically capitalized, and objects are instantiated from it with the use of the new keyword.  Within this constructor function, the keyword this is frequently used.  This is always a contextual keyword; it represents different objects depending on the context in which it is used.  In the case of a constructor function, this means “the object that is being instantiated by using this function”. 

To illustrate, here is a simple example of a Teacher class.  This constructor accepts three parameters, which are assigned as values to named properties in the instantiated object.

JavaScript
function Teacher(title, lastName, grade) {
   this.title    = title;
   this.lastName = lastName;
   this.grade    = grade;
}

var myFavorite = new Teacher("Ms.", "Morrison", 3);
alert(myFavorite.grade);        // displays "3"

An object does not have to own a named value for code to reference one.  The following line adding to our example is perfectly acceptable JavaScript code:

JavaScript
var s = myFavorite.schoolName;
alert(s);   // displays "undefined"

There is no syntax error, no “property ‘schoolName’ does not exist” exception thrown.  The value returned for the variable s is simply undefined

When JavaScript encounters a reference to a property (a named value), it first looks to the named values that have been included with the object.  If it doesn’t find the desired name, it then looks to the object’s prototype.  The special prototype property of a JavaScript object is an object in its own right.  Assigning a value to a property of the Teacher prototype makes that value available to all Teacher objects.

JavaScript
Teacher.prototype.schoolName = "Rockbrook Elementary";
alert(myFavorite.schoolName);  // displays "Rockbrook Elementary"

The concept of a method -- an action that may be performed by an object – is implemented by assigning a function as a named value in the prototype for the class.  In our simple example, here is a getName() method defined on our Teacher class:

JavaScript
Teacher.prototype.getName = function() {
    return this.title + " " + this.lastName;
}

alert(myFavorite.getName());  // displays "Ms. Morrison"

A method may also be assigned within the constructor function like other named values, using syntax like this.getName = function() {…}.  If defined this way though, a new copy of the function is created every time a Teacher object is instantiated.  It is more efficient to define the function once on the Teacher.prototype object; because of prototype-based inheritance, the method is available to all instantiated Teacher objects.

Class-like inheritance and subclasses

Languages that support class-based inheritance afford the ability to establish subclasses – a class that inherits functionality from another class, usually to extend or override its functionality for specific cases.  In bowling for example, the display of the final frame is slightly different than the display of earlier frames.  The DisplayFrame class in the MishaBowling.Core.js file establishes the display instructions for a “normal” frame.  The DisplayFinalFrame class establishes the display instructions for the last frame of the game.  The two are defined such that DisplayFinalFrame behaves like a subclass to DisplayFrame

Though JavaScript does not feature class-based inheritance natively, prototype-based inheritance is still a powerful aspect of the language.  Objects may inherit functionality from the prototype object, which itself may have a prototype object providing inherited values, and so on up a prototype chain.  The tutorial Inheritance and the prototype chain from Mozilla provides a good explanation[5].  With attention to a few details, we can leverage the prototype object to establish functioning subclasses. 

Recall that our DisplayFrame is structured information that is derived by interpreting the data from a ScoringFrame, which itself is derived from a sequenced list of rolls (operational data).  The DisplayFrame constructor function includes code that uses this to initialize the values of the instantiating object’s scoringFrame, score and isCurrent properties.  (Note: for simplicity, the example here is slightly different than how the DisplayFrame class is written in the accompanying code, where a namespacing strategy is used.  We will discuss namespacing later in the article). 

JavaScript
function DisplayFrame (scoringFrame, valueForStrike, strikeDisplay, spareDisplay
                        , zeroDisplay)
{   
    this.scoringFrame = scoringFrame;   
   
    // the running score is displayed in the frame, depending on whether the scoring
    // for the frame is complete.  If not complete, the running score is blank
    this.score = !scoringFrame.isComplete ? "" : scoringFrame.runningScore.toString();

    // and upon construction, populate display slot properties from the rolls
    // in the scoring frame
    this.populateSlots(valueForStrike, strikeDisplay, spareDisplay, zeroDisplay); 

    // initialize the isCurrent property; the parent DisplayFrameList will mark
    // the appropriate frame "current" as it finishes its processing
    this.isCurrent = false;
}

The populateSlots() method referenced in the constructor is defined on the prototype object for the class.  This method contains the logic to populate two display “slots” in a frame each with the numeric or alphabetic symbol appropriate for scorecard marking based on its associated scoring frame. 

JavaScript
DisplayFrame.prototype.populateSlots = function(valueForStrike, strikeDisplay
                                          , spareDisplay, zeroDisplay)
{
    var sf = this.scoringFrame;
   
    // display slot1:  blank if not rolled yet, blank on a strike,
    //    the "zero" character on a 0 value, or otherwise the roll number
    this.slot1 = sf.roll1 === null           ? ""
               : sf.roll1 === valueForStrike ? ""
               : sf.roll1 === 0              ? zeroDisplay
               : sf.roll1.toString();
              
    // slot2:  strike mark if first roll strike; blank if first or
    //         second roll not yet rolled; spare mark if first and
    //         second rolls form a spare; the "zero" character
    //         on a 0 value for the second roll; otherwise the roll number
    this.slot2 = sf.roll1 === valueForStrike            ? strikeDisplay        
               : sf.roll1 === null || sf.roll2 === null ? ""
               : sf.roll1 + sf.roll2 === valueForStrike ? spareDisplay
               : sf.roll2 === 0                         ? zeroDisplay
               : sf.roll2.toString();
              
    // slot3:  in a normal frame, there is no "slot 3". 
    this.slot3 = "";     

}

The constructor function for the subclass DisplayFinalFrame is simple.  Using the call() method, the subclass references the base class’ constructor.

JavaScript
function DisplayFinalFrame(scoringFrame, valueForStrike, strikeDisplay, spareDisplay
                            , zeroDisplay)
{
    // call the constructor for the base class
    DisplayFrame.call(this, scoringFrame, valueForStrike, strikeDisplay, spareDisplay
                       , zeroDisplay);   
}

Using the call method in JavaScript in this context is very similar to using the base keyword in C#, or the super keyword in Java.  It executes the constructor method of our base DisplayFrame class, ensuring that properties set in its constructor are applied in our subclass too.  To establish a proper subclass, we take one additional step to ensure that anything applied to DisplayFrame class’ prototype object, whether methods or properties, is also applied to the prototype of the subclass DisplayFinalFrame, with this line of code:

JavaScript
DisplayFinalFrame.prototype = Object.create(ns.DisplayFrame.prototype);

Essentially we are overriding what JavaScript would give our DisplayFinalFrame class as a default prototype with a new instantiation of one that is created as a copy of that of the base class.  While this does let us enjoy all the methods (and properties) that may be assigned to the base class’ prototype, something we need for a true subclass, it also does one unfortunate thing.  It replaces our subclass constructor function.

JavaScript assigns the class constructor function itself to a constructor property of the prototype object.  In replacing the prototype object for our subclass, we copy everything from the prototype of the original class.  This includes the original class’ constructor function.  At this point, if we created a DisplayFinalFrame object, it would think it was a DisplayFrame object instead.  So with one more line of code, we re-establish the constructor for the subclass:

JavaScript
DisplayFinalFrame.prototype.constructor = DisplayFinalFrame;

With those three elements – the subclass constructor function issuing a call() method to the base class; copying the base class’ prototype object to the subclass; and resetting the subclass’ constructor property to its own constructor function – we effectively simulate a class/subclass inheritance relationship.  From that point, we can add additional methods to the prototype of the subclass, or override any desired methods from the base class.  In the case of DisplayFinalFrame, we override the base populateSlots() method to implement the special marking logic that distinguishes the final frame from previous frames.  We also populate a third display slot which is irrelevant in other frames.

JavaScript
DisplayFinalFrame.prototype.populateSlots = function(valueForStrike, strikeDisplay
                          , spareDisplay, zeroDisplay)
{
    var sf = this.scoringFrame;   

    // display slot 1
    this.slot1  = sf.roll1 === null           ? ""
                : sf.roll1 === valueForStrike ? strikeDisplay
                : sf.roll1 === 0              ? zeroDisplay
                : sf.roll1.toString();               

    // display slot 2
    this.slot2 = sf.roll1 === null || sf.roll2 === null ? ""
               : sf.roll1 === valueForStrike && sf.roll2 === valueForStrike
                     ? strikeDisplay
               : sf.roll1 !== valueForStrike && sf.roll1 + sf.roll2 === valueForStrike
                     ? spareDisplay
               : sf.roll2 === 0 ? zeroDisplay
               : sf.roll2.toString();
              
    // display slot 3: special for the final frame
    this.slot3 = sf.roll1 === null || sf.roll2 === null || sf.roll3 === null
                      ? ""
               : sf.roll1 === valueForStrike && sf.roll2 !== valueForStrike
                  && sf.roll2 + sf.roll3 === valueForStrike
                  ? spareDisplay
               : sf.roll3 === valueForStrike ? strikeDisplay
               : sf.roll3 === 0 ? zeroDisplay
               : sf.roll3.toString();
}

 

Namespacing approach:  MishaBowling

JavaScript is remarkably flexible.  While this makes the language very powerful, it can also empower bad organizational habits.  A developer can code in JavaScript with no concern for architecture at all, and JavaScript says, “okay, go for it!”  It is up to the JavaScript programmer to bring a sense of architectural discipline to make the resulting code organized and manageable.  One helpful strategy is namespacing.

The concept of a namespace is native to languages like C# and Java.  With a namespace, classes are always defined under a specific named context.  This prevents conflicts among objects of different types that otherwise share the same name.  You may have a Game class for both your bowling and your football code for example.  A namespace helps distinguish them:  Bowling.Game vs. Football.Game

Applying a namespacing strategy within JavaScript is also a helpful means for organizing code.  There are many approaches to JavaScript namespacing, with several summarized well in the article Namespacing in JavaScript by Angus Croll[6].  My approach here is similar to #5 in this article, making use of the keyword this as representing a namespace object in an anonymous function.  Let’s look at MishaBowling.Core.js, the file that defines the core classes necessary to support scoring.  At the top of the code are the following lines:

JavaScript
// Establish the namespace "MishaBowling" if it doesn't already exist.  The
// namespace is essentially a globally scoped object named MishaBowling, within which
// our classes, methods, and other nested namespaces will be defined
if (typeof MishaBowling === 'undefined' )
    var MishaBowling = {};

// ... and establish the MishaBowling.Core namespace if it doesn't already exist
if (typeof MishaBowling.Core === 'undefined')   
    MishaBowling.Core = {};

A global-level variable named MishaBowling is defined first.  This is the only global-level variable produced by the full set of code, with MishaBowling serving as the chosen namespace.  The second line establishes a nested object named Core.  All of our relevant scoring code is organized in this MishaBowling.Core namespace.

The pattern continues with an anonymous function operating as the container for all our MishaBowling.Core code:

JavaScript
(function() {

    ...
    ... all the rest of the MishaBowling.Core code ...
    ...

}).apply(MishaBowling.Core);

When processed by the JavaScript engine, the .apply() method on the anonymous function ensures its argument object (our MishaBowling.Core namespace object) is populated with its contents.  We would then instantiate objects from classes defined in this MishaBowling.Core object using the full namespace as in this example:

JavaScript
var myGame = new MishaBowling.Core.Game(...);

As a first step within the anonymous namespace function, I allow myself a property of convenience.  To make it easier for a developer to differentiate among the different contexts in which the keyword this represents different objects, I add a property ns to the namespace object and assign to it this which, given its context, represents the namespace object itself:

JavaScript
(function() {

    var ns = this;

    ...

}).apply(MishaBowling.Core);

For clarity, I then use the ns variable within the anonymous function as a proxy to mean “this namespace object”.  For example, the code that defines the ScoringFrame class follows the ns variable declaration and is itself prefixed by ns:

JavaScript
(function() {

    var ns = this;

    ...

    ns.ScoringFrame = function(...)
    {
      ... class constructor code ...
    }

    ...

}).apply(MishaBowling.Core);

In this way, the ScoringFrame and other classes become properties of the MishaBowling.Core object, and are therefore contextualized as part of the namespace.

Summary

With appreciation that data have meaning only when understood in a context, a business intelligence practitioner interprets data from an operational context to bring meaningful answers to strategic questions in an informational context.  When this approach is applied to an application for scoring a bowling game, the operational data that is a sequenced list of rolls may be interpreted to derive scoring frames, which themselves may be interpreted to derive the display markings that would appear on a traditional scorecard.  Though JavaScript is prototype-based in its approach to objects and inheritance, it is flexible enough to support a design that organizes code in a more traditional class-based manner.  A developer may further organize JavaScript code by adopting a namespacing strategy that is otherwise a common design feature of many object-oriented languages.  In the same way that BI professionals may bring structure to the process of deriving the informational from the operational, so developers may bring thoughtful architectural strategies to JavaScript code. 

References

[1] Wikipedia, Ten-pin bowlinghttps://en.wikipedia.org/wiki/Ten-pin_bowling

[3] Howard Dresner is generally credited with coining the modern use of the term business intelligence in 1989.  https://en.wikipedia.org/wiki/Business_intelligence

[4] The Association for Institutional Research was formed in 1966 to support higher education professionals engaged in in the derivation of meaningful information from institutional data, and its strategic use for effective decision making.  http://www.airweb.org/AboutUs/Pages/AboutAIR.aspx

[5] Mozilla Developer Network web docs, Inheritance and the prototype chain:  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

License

This article, along with any associated source code and files, is licensed under The MIT License