Exercise index of this lecture   Alphabetic index   Course home   

Exercises and solutions
Classes and Objects


3.1   Yahtzee  

Write a very simple Yahtzee program based on the Die class. Yahtzee is played by use of five dice that are tossed simultaneously. The players are supposed to fill out a table of results. The table should allow registration of ones, ..., sixes, three-of-a-kind, four-of-a-kind, full-house, small-straight, large-straight, yahtzee, and chance. See wikipedia for more details and inspiration.

Be sure to use the version of the Die class that shares the Random class with the other dice. This version of the Die class is produced in another exercise in this lecture.

This program is for a single player, who tosses the five dice repeatedly. Each time the five dice are tossed a table cell is filled. No re-tossing is done with less than five dice. The first category that fits a given toss is filled. (Try yahtzee first, Chance last). Keep it simple!

You may consider to write a YahtzeeTable class which represents the single user table used to register the state of the game. Consider the interface and operations of this class.


3.2   Time Classes  

This is not a programming exercise, but an exercise which asks you to consider data and operations of classes related to time.

Time is very important in our everyday life. Therefore, many of the programs we write somehow deal with time.

Design a class PointInTime, which represents a single point in time. How do we represent a point in time? Which variables (data) should be encapsulated in the class? Design the variables in terms of their names and types.

Which operations should constitute the client interface of the class? Design the operations in terms of their names, formal parameters (and their types), and the types of their return values.

Can you imagine other time-related classes than PointInTime?

Avoid looking at the time-related types in the C# library before you solve this exercise. During the course we will come back the time related types in C#.


3.3   Public data representation  

In the accompanying Point and Point client classes the data representation of Point is available to the client class. This may be tempting for the programmer, because we most likely wish to make the x and y coordinates of points available to clients.

Why is it a bad solution? It is very important that you can express and explain the problem to fellow programmers. Give it a try!

Now assume that we are forced (by the boss) to change the data representation of Point. As a realistic scenario, we may introduce polar coordinates instead of the rectangular x and y coordinates. Recall that polar coordinates consist of a radius and an angle (in radians or degrees).

What will happen to client classes, such as this client, when this change is introduced? Is it an easy or a difficult modification to the given client class? Imagine that in a real-life situation we can have thousands of similar lines of code in client programs that refer to x and y coordinates.

Rewrite selected parts of class Point such that the client "survives" the change of data representation. In your solution, the instance variables should be private in the Point class. Are you able to make a solution such that the client class should not be changed at all?

The private static methods at the bottom of this version of class Point will most likely be helpful. In addition, this version of class Point serves as an initial (but incomplete and somewhat problematic) template of the Point class that you are supposed to write.

The client class of Point calculates the distances between pairs of points. This is not a good idea because far too many details occur repeatedly in the client. Suggest a reorganization and implement it.

Solution

The solution below is programmed with C# properties

// A versatile version with Rotation and internal methods
// for rectangular and polar coordinates. 

using System;

public class Point {

  public enum PointRepresentation {Polar, Rectangular}

  // public double x, y;   // previous public data repr.
  private double r, a;     // new, private data repr: radius, angle

  public Point(double x, double y){
     r = RadiusGivenXy(x,y);
     a = AngleGivenXy(x,y);
  }

  public Point(double par1, double par2, PointRepresentation pr){
   if (pr == PointRepresentation.Polar){
     r = par1; a = par2;
   } 
   else {
     r = RadiusGivenXy(par1,par2);
     a = AngleGivenXy(par1,par2);
   }
  }

  public double x {
    get {return XGivenRadiusAngle(r,a);}
  }

  public double y {
    get {return YGivenRadiusAngle(r,a);}
  }

  public double Radius {
    get {return r;}
  }

  public double Angle{
    get {return a;}
  }


  public void Move(double dx, double dy){
    double x, y;
    x = XGivenRadiusAngle(r,a);   y = XGivenRadiusAngle(r,a); 
    r = RadiusGivenXy(x+dx, y+dy);
    a = AngleGivenXy(x+dx, y+dy);
  }

  public void Rotate(double angle){
    a += angle;
  }

  public override string ToString(){
    return  "(" + XGivenRadiusAngle(r,a) + "," + 
                  YGivenRadiusAngle(r,a) + ")";
  }

  private static double RadiusGivenXy(double x, double y){
    return Math.Sqrt(x * x + y * y);
  }

  private static double AngleGivenXy(double x, double y){
    return Math.Atan2(y,x);
  }

  private static double XGivenRadiusAngle(double r, double a){
    return r * Math.Cos(a);
  }

  private static double YGivenRadiusAngle(double r, double a){
    return r * Math.Sin(a);
  }

  
}

  


3.4   How private are private instance variables?  

The purpose of this exercise is to find out how private private instance variables are in C#.

Given the BankAccount class. Now modify this class such that each bank account has a backup account. For the backup account you will need a new (private) instance variable of type BankAccount. Modify the Withdraw method, such that if there is not enough money available in the current account, then withdraw the money from the backup account. As an experiment, access the balance of the backup account directly, in the following way:

   backupAccount.balance -= ... 

Is it possible to modify the private state of one BankAccount from another BankAccount? Discuss and explain your findings. Are you surprised?

Solution

It is possible for one BankAccount object to modify the balance of another BankAccount object. The version of class BankAccount shown below demonstrates this. Please notice the line

  backupAccount.balance -= amount - balance;

in the method withdraw.

Is this reasonable? Yes. Our observations pertain to objects in the running program - the dynamic situation. Our attention to firewalls (icebergs and representation independence) is oriented towards the source program - the static situation. In relation to programmers, who write the BankAccount class and related classes, it is important to enforce the discipline that the data representation of foreign classes is invisible. Without such a policy the idea of representation independence will be shipwrecked. This is a software engineering concern.

It causes no software engineering problems if one BankAccount object can modify the data of another BankAccount object in the way sketched above.

Here follows my programmed solution to this exercises:

using System;

public class BankAccount {

   private double interestRate;
   private string owner;
   private double balance;
   private BankAccount backupAccount;

   public BankAccount(string owner, double interestRate, 
                      BankAccount backupAccount) {
      this.interestRate = interestRate;
      this.owner = owner; 
      this.balance = 0.0;
      this.backupAccount = backupAccount;
   }

   public BankAccount(string owner, double interestRate): 
     this(owner, interestRate, null){
   }   

   public BankAccount(string owner): this(owner, 0.0) {
   }

   public double Balance () {
      return balance;
   }

   public void withdraw (double amount) {
      if (balance >= amount)
         balance -= amount;
      else if (balance < amount && backupAccount != null){
         backupAccount.balance -= amount - balance;
         balance = 0; 
      }
      else throw new Exception("Help!");
   }

   public void deposit (double amount) {
      balance += amount;
   }

   public void addInterests() {
      balance = balance + balance * interestRate;
   }    

   public override string ToString() {
      return owner + "'s account holds " +
            + balance + " kroner";
   }
} 


3.5   The method LogTransaction in class BankAccount  

In the accompanying BankAccount class we have sketched and used a private method named LogTransaction. Implement this private method and test it with the BankAccount client class.

Solution

Here is my version of BankAccount class with logging:

using System;

public enum AccountTransaction {Withdrawing, Depositing, Interests};

public class BankAccount {

   private double interestRate;
   private string owner;
   private decimal balance;

   const int logMaxSize = 1000;
   private static LogEntry[] log = new LogEntry[logMaxSize];
   private static int nextLogIndex = 0;

   public BankAccount(string owner): this(owner, 0.0) {
   }

   public BankAccount(string owner, double interestRate) {
     this.interestRate = interestRate;
     this.owner = owner; 
     this.balance = 0.0M;
   }   

   public decimal Balance () {
     return balance;
   }

   private void LogTransaction(AccountTransaction kind, decimal amount){
      if (nextLogIndex < logMaxSize){
        log[nextLogIndex] = new LogEntry(this, kind, DateTime.Now, amount);
        nextLogIndex++;
      } else
        throw new Exception("Problems");
   }

   public static void FullLogHistory(){
     for(int i = 0; i < nextLogIndex; i++)
       Console.WriteLine(log[i]);
   }

   public void Withdraw (decimal amount) {
     this.LogTransaction(AccountTransaction.Withdrawing, amount);
     balance -= amount;
   }

   public void Deposit (decimal amount) {
     this.LogTransaction(AccountTransaction.Depositing, amount);
     balance += amount;
   }

   public void AddInterests() {
     decimal interests = balance * (decimal)interestRate;
     this.LogTransaction(AccountTransaction.Interests,  interests);
     balance += interests;
   }    

   public override string ToString() {
     return owner + "'s account holds " +
           + balance + " kroner";
   }
} 

public class LogEntry{
  private BankAccount ba;
  private AccountTransaction kind;
  private DateTime timestamp;
  private decimal amount;

  public LogEntry(BankAccount ba, AccountTransaction kind, DateTime t, decimal amount){
    this.ba = ba;
    this.kind = kind;
    this.timestamp = t;
    this.amount = amount;
  }

  public override string ToString(){
    return "LogEntry " + kind + ": " + amount;
  }
} 

Note to the solution: Instead of passing the current time as a parameter to LogTransaction, I found it more reasonable to capture the time inside the body of LogTransaction.


3.6   Course and Project classes  

In this exercise you are asked to program three simple classes which keep track of the grading of a sample student. The classes are called BooleanCourse, GradedCourse, and Project.

A BooleanCourse encapsulates a course name and a registration of passed/not passed for our sample student.

A GradedCourse encapsulates a course name and the grade of the student. For grading we use the Danish 7-step, numerical grades 12, 10, 7, 4, 2, 0 and -3. You are also welcome use the enumeration type ECTSGrade from an earlier exercise. The grade 2 is the lowest passing grade.

In both BooleanCourse and GradedCourse you should write a method called Passed. The method is supposed to return whether our sample student passes the course.

The class Project aggregates two boolean courses and two graded courses. You can assume that a project is passed if at least three out of the four courses are passed. Write a method Passed in class Project which implements this passing policy.

Make a project with four courses, and try out your solution.

In this exercise you are supposed to make a simple and rather primitive solution. We will come back to this exercise when we have learned about inheritance and collection classes.

Solution

Here is my solution:

using System;

public class BooleanCourse{

  private string name;
  private bool grade;

  public BooleanCourse(string name, bool grade){
    this.name = name;
    this.grade = grade;
  }

  public bool Passed(){
    return grade;
  }

}

public class GradedCourse{

  private string name;
  private int grade;

  public GradedCourse(string name, int grade){
    this.name = name;
    this.grade = grade;
  }  

  public bool Passed(){
    return grade >= 2;
  }

}

public class Project{

  private BooleanCourse c1, c2;
  private GradedCourse c3, c4;

  public Project(BooleanCourse c1, BooleanCourse c2, 
                 GradedCourse c3, GradedCourse c4){
    this.c1 = c1; this.c2 = c2; 
    this.c3 = c3; this.c4 = c4;
  }

  public bool Passed(){
    return 
     (c1.Passed() && c2.Passed() && c3.Passed() && c4.Passed()) ||
     (!(c1.Passed()) && c2.Passed() && c3.Passed() && c4.Passed()) ||
     (c1.Passed() && !(c2.Passed()) && c3.Passed() && c4.Passed()) ||
     (c1.Passed() && c2.Passed() && !(c3.Passed()) && c4.Passed()) ||
     (c1.Passed() && c2.Passed() && c3.Passed() && !(c4.Passed()));
  }

}

public class Program {

  public static void Main(){
    BooleanCourse c1 = new BooleanCourse("Math", true),
                  c2 = new BooleanCourse("Geography", true);
    GradedCourse  c3 = new GradedCourse("Programming", 0),
                  c4 = new GradedCourse("Algorithms", -3);

    Project p = new Project(c1, c2, c3, c4);

    Console.WriteLine("Project Passed: {0}", p.Passed());
  }

}

In several respects, the program shown above is tedious:

  • We have not been able to treat the two kinds of courses, and their common method Passed, together. With use of inheritance we will be able to write a better program.
  • It is not easy to program the passing method in class Project. With use of collection classes (such as List) we will be able to make a collection of courses. In addition, we will be able to program the passing rule (the 3 out of 4 rule) more smoothly, and in a more general way.


3.7   Sharing the Random Generator  

In the Die class shown in the start of this lecture, each Die object creates its own Random object.

We observed that tosses of two or more instances of class Die will be identical. Explain the reason of this behavior.

Modify the Die class such that all of them share a single Random object. Consider different ways to implement this sharing. Rerun the Die program and find out if "the parallel tossing pattern" observed above has been alleviated.

Solution

The solution which I provide is based on the Singleton design pattern, which we will discuss later in the teaching material. I make my own Random class, which cannot be instantiated because it is a singleton. Thus, you should use Random.Instance() instead of new Random(...).

using System;

public class Random {

  // Singleton pattern:
  // Keeps track of unique instance of this class
  private static Random uniqueInstance = null;

  // Holds the instance of System.Random
  private System.Random systemRandom;

  // Singleton pattern: Private constructor.
  private Random(){
    systemRandom = new System.Random(unchecked((int)DateTime.Now.Ticks));
  }

  public static Random Instance(){
    if (uniqueInstance == null)
      uniqueInstance = new Random();
    return uniqueInstance;
  }

  public int Next(int lower, int upper){
    // delegate to systemRandom
    return systemRandom.Next(lower,upper);
  }

}

Notice that uniqueInstance is a class class variable (static variable) in class Random. Notice also that the method called Instance is a class method (static method).


Generated: Monday February 7, 2011, 12:13:42