Copyright © 2012 David Schmidt

Lecture 2:
Classes model virtual reality


2.1 Attributes, abilities, and responsibilities
    2.1.1 Identifying entities, attributes, abilities, and responsibilities
2.2 Class format
2.3 Unit Testing
    2.3.1 How to perform and save unit tests in Visual Studio


Here is a slightly provocative statement: A software scientist builds a virtual reality --- "an electronic version of real-life" --- in a computer.

For example, a spreadsheet program is a virtual sheet of grid paper that includes a helper "gnome" who adds up the rows and columns each time a human enters a number into the grid. A computerized game (card game, Dungeons and Dragons, etc.) is a virtual reality of terrain and individuals, some of which are controlled by humans, some of what are not. A database program is a virtual filing cabinet whose file folders are linked together with complex virtual strings and clips.

Now, if you want to build a virtual reality inside a computer, you much ask what the entities will be. You design their blueprints, and you activate them --- bring them to life, as many as you want.

Classes are used in object-oriented languages to define the "blueprints"/"genetic codes" and from them we construct objects.


2.1 Attributes, abilities, and responsibilities

A real-life dog is an entity that has attributes (gender, breed, height, weight, maybe a name) and abilities (can bark, can bite, can eat, can run, can fetch newspaper, can wag tail). A "responsibility" is an activity that an entity must do; a dog might have the responsibility to bark when there is an intruder. Even real-life entities that we consider inanimate have attributes and abilities: A car is inanimate, has lots of attributes, and has the ability to propel a human to Chicago or New York. (A car is unlikely to have responsibilities, however.) When we computerize dogs and cars, their attributes, abilities, and responsibilities will be coded in class definitions as fields, methods, and commands (within the methods).

Now consider a card game: In a card game, a "card" is an entity with attributes suit (spades, hearts, diamonds, clubs) and count (2, 3, 4, ..., king, ace). A card can't do much --- maybe "tell" you its suit and count. It has no responsibilities, either. A "card deck" is an entity that holds cards inside it, can "shuffle itself" and can yield ("deal") cards, one at a time. A deck has no responsibilities --- other entities, who do have resposibilities, use it to fulfill their responsibilities. Other entities in a card game are the dealer (who owns the card deck, who can deal from it, and who has responsibility of enforcing the rules of the game) and the players (each one owns a "hand" of cards, can play the game, and must decide whether to add or remove cards from the hand). The dealer has the responsibility of asking players if they want cards and then deal cards to players. A player can retain or discard the cards in its hand. We code these entities in class definitions.


2.1.1 Identifying entities, attributes, abilities, and responsibilities

Virtual reality is fascinating because what we consider as inanimate objects in the real world are "alive" in the virtual world. And there are concepts that are not entities in the real world that exist as entities in the virtual world, e.g., a "hand" of cards. We must visualize the virtual world that we build within the computer before we type a line of code. We must make a design before we build.

Watch this video of a round of Blackjack. What are the entities? What are the classes? How are the entities associated together?

How to play blackjack, part 1.


2.2 Class format

I trust you know how to define a class. Here is an example, of a playing card, which might be coded within a Visual Studio Solution:
===================================================

// A _NAMESPACE_ IS THE SAME THING AS A VISUAL STUDIO PROJECT:
namespace CardConcepts {

  // AN _ENUMERATION_ IS A NEW "PRIMITIVE DATA TYPE" THAT YOU DEFINE:
  // enumerations of the Suits and Counts that a playing card can have:
  public enum Suit { Spades, Hearts, Diamonds, Clubs };
  public enum Count { Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King };

  // THIS IS A _CLASS_:
  // class for constructing a playing card
  public class Card {
    public readonly Count count;  // THIS IS A _FIELD_; SO IS THE LINE BELOW:
    public readonly Suit suit;    // "readonly"  means var's value cannot change

    // THIS IS A _CONSTRUCTOR METHOD_; IT TAKES TWO _PARAMETERS_:
    public Card(Count a, Suit b) { count = a;  suit = b; }

    // THIS IS A _METHOD_; IT RETURNS AN ANSWER:
    // returns the int value of a card in Blackjack
    public int BJvalue() {
      int i = (int)count + 1;   // obtain int value that underlies the Count
      if (i > 10) { i = 10; }   // in Blackjack, face cards have value 10
      return i;
    }

    // returns the string value of a card;  overrides default  ToString  coding
    public override string ToString() { return count + " of " + suit; }
  } 
}

===================================================
(Make certain you understand all the constructions and all the comments in the above example. Listen to the instructor blab about them or do a web search. But make certain you understand everything!)

The fields defined in class Card list the attributes, count and suit; the methods are the abilities of a Card. Although inanimate, a card can tell you its Blackjack value and its name (a string).

The class is saved in a Visual Studio project named CardConcepts. In the main program, we construct objects,

public static void Main() {
    Card c1 = new Card(Count.Ace, Suit.Hearts);
    Console.WriteLine("card {0} has value {1}", 
                        c1.ToString(), c1.BJvalue());
    if (c1.suit == Suit.Clubs) {
        Console.WriteLine("it's a club!"); }
}
and the card entities come to (virtual, electrical) life.


2.3 Unit Testing

A program is an assembly of objects constructed from classes. A class by itself is not a program and might not "execute" all alone. This is a serious problem in modern software engineering --- if we develop a complex software system in stages, we want to design, build, and test the components (classes) one at a time, so that when we assemble them, we have high confidence that the assembly operates as desired.

How do we test and validate the quality of a class that cannot "execute" all alone?

There are special tools to test a class alone, but it is easy to write a test harness for unit testing an individual class. (Like its name suggests, "unit testing" is the testing of one unit of a system.)

A unit test of a class is a script of actions that use one or more objects constructed from the class. The script exercises the attributes and methods. There should be enough unit tests that all the attributes and methods are exercised, in all possible, significant orders.

That last phrase is important --- some classes are defined with multiple methods, and the methods are supposed to be used in a certain order. Example:

class TextFile {
    private string filename;
    private FileStream address_on_disk;
    private Mode mode;   // should be  Mode.Read  or  Mode.Write

    public TextFile(string name, Mode m) { 
      ...code to initialize attribute and open the file
    }

    public string ReadLine() {
       ... code to read textline from filename, provided mode == Mode.Read ...
    }

    public string WriteLine() {
       ... code to write textline from filename, provided mode == Mode.Write ...
    }

    public bool CloseFile() { ... }
}
The unit tests for class TextFile should include scripts that properly construct ("open") a new file object, read it repeatedly and close it, as well as scripts that call methods in the wrong order (e.g., construct, close, read) as well as scripts that call methods improperly (e.g., construct a read-file and then write to it).

The unit tests should be collected together and used and reused while the class is coded, altered, and improved. The unit tests for a "validation suite" for establishing confidence in the quality of the class code.


2.3.1 How to perform and save unit tests in Visual Studio

The details are found here, in the Visual Studio reference notes.. For our card example, say we coded class Card in Visual Studio in Solution CardConcepts. If you look closely in VS's Solution Explorer window, you will see that Solution Card Concepts holds one Project, also named CardConcepts. And within the project is a file, Card.cs, that holds class Card.

We write and save the unit tests for Project CardConcepts in a new, separate project, call it UnitTests, which will be stored also within Solution CardConcepts. (See Visual Studio notes to learn how.)

Here is an example of a test harness (the Main procedure of project UnitTests), ready to run:

===================================================

using System;
 ...
using CardConcepts;  // ADD THIS LINE and remember to ADD REFERNCE IN VS

namespace UnitTests {
  class Program {

    // executes unit tests of the classes in CardConcepts
    public static void Main() {
      UnitTest1();
      UnitTest2();
      ... and so on ...
    }

    // executes a unit test of class Card
    static void UnitTest1() {
      Card c1 = new Card(Count.Ace, Suit.Hearts);
      Console.WriteLine("card {0} has value {1}", 
                        c1.ToString(), c1.BJvalue());
      Console.ReadLine();
    }

   static void UnitTest2() {
       ... and so on ... }
  }
}

===================================================
This code lives in Program.cs within Project UnitTests within Solution CardConcepts.

To run the unit tests, you must reset the "Startup project" to UnitTests and start the debugger. See Visual Studio notes to learn how.

Exercise