Team LiB
Previous Section Next Section

Composite Classes, Revisited

You may recall that when we talked about the attributes of the Student class back in Chapter 3, we held off on assigning types to a few of the attributes, as shown in Table 6-1.

Table 6-1: Proposed Data Structure for the Student Class

Attribute Name

Data Type

name

string

studentID

string

birthdate

System.DateTime

address

string

major

string

gpa

double

advisor

Professor

courseLoad

???

transcript

???

Armed with what we now know about collections, we can go back and assign types to attributes courseLoad and transcript.

courseLoad

The courseLoad attribute is meant to represent a list of all Course objects that the Student is presently enrolled in. So, it makes perfect sense that this attribute be declared to be simply a standard collection of Course objects!

public class Student
{
  private string name;
  private string studentId;
  private CollectionType courseLoad; // of Course objects
  // etc.

transcript

The transcript attribute is a bit more challenging. What is a transcript, in real-world terms? It's a report of all of the courses that a student has taken since he or she was first admitted to this school, along with the semester in which each course was taken, the number of credit hours that each course was worth, and the letter grade that the student received for the course. If we think of each entry in this list as an object, we can define them via a TranscriptEntry class, representing an abstraction of a single line item on the transcript report, as follows:

public class TranscriptEntry
{
  // One TranscriptEntry represents a single line item on a transcript report.
  private Course courseTaken;
  private string semesterTaken;  // e.g., "Spring 2000"
  private string gradeReceived;  // e.g., "B+"

  // Other details omitted ...

  // Note how we "talk to" the courseTaken object via its methods
  // to retrieve some of this information (delegation once again!).
  public void PrintTranscriptEntry() {
    // Reminder: \t is a tab character.
    Console.WriteLine(courseTaken.CourseNo + "\t" +
        courseTaken.Title + "\t" +
        courseTaken.CreditHours + "\t" +
        gradeReceived);
  }

  // etc.
}

Note that we're declaring one of the attributes of TranscriptEntry to be of type Course, which means that each TranscriptEntry object will maintain a handle on its corresponding Course object. By doing this, the TranscriptEntry object can avail itself of the Course object's title, course number, or credit hour value (needed for computing the GPA)—all privately encapsulated in the Course object as attributes—by accessing the appropriate properties on that Course object as needed.

Back in the Student class, we can now define the Student 's transcript attribute to be a collection of TranscriptEntry objects. We can then add a PrintTranscript method to the Student class, the code for which is highlighted in the following snippet:


public class Student
{
  private string name;
  private string studentId;
  // Pseudocode.
  private CollectionType transcript; // of TranscriptEntry objects
  // etc.

  // Details omitted ...

  public void PrintTranscript() {
    // Pseudocode.
    for (each TranscriptEntry t in transcript) {
      t.PrintTranscriptEntry();
    }
  }
}

transcript, Take 2

Alternatively, we could use the technique of creating a wrapper class called Transcript to encapsulate some standard collection type, as we did with EnrollmentCollection in an earlier example:

public class Transcript
{
    private ArrayList transcriptEntries; // of TranscriptEntry objects
    // other attributes omitted from this example

    // Pseudocode.
    public void AddTranscriptEntry(arglist) {
      insert new entry into the transcriptEntries ArrayList -- details omitted
    }

    // We've transferred the logic of the Student class's PrintTranscript
    // method into THIS class instead.
    public void PrintTranscript() {
      // Pseudocode.
      for (each TranscriptEntry t in transcriptEntries) {
        t.PrintTranscriptEntry();
      }
    }

    // etc.
  }

We then can go back to the Student class and change our declaration of the transcript attribute from being a standard collection type to being of type Transcript:

public class Student
{
  private string name;
  private string studentId;
  // etc.
  private Transcript transcript;  // an ENCAPSULATED collection of
                                     // TranscriptEntry objects
  // etc.

We can then turn around and simplify the PrintTranscript method of the Student class accordingly:

public class Student
{
  // Details omitted.

  public void PrintTranscript(string filename) {
    // We now delegate the work to the Transcript attribute!
    transcript.Print();
  }

  // etc.

This "take 2" approach of introducing two new classes/abstractions— TranscriptEntry and Transcript —is a bit more sophisticated than the first approach, where we only introduced TranscriptEntry as an abstraction. Also, this second approach is "truer" to the object paradigm, because the Student class needn't be complicated by the details of how Transcripts are represented or managed internally—those details are hidden inside of the Transcript class, as they should be.

Our Completed Student Data Structure

Table 6-2 illustrates how we've taken full advantage of collections to round out our Student class definition.

Table 6-2: Rounding Out the Student Class's Data Structure with Collections

Attribute Name

Data Type

name

string

studentID

string

birthdate

System.DateTime

address

string

major

string

gpa

double

advisor

Professor

courseLoad

Standard type collection of Course objects

transcript

Standard type collection of TranscriptEntry objects or (preferred) Transcript


Team LiB
Previous Section Next Section