Main Page

Previous Next

Designing Classes

A basic problem in object-oriented programming is deciding how the classes in your program should relate to one another. One possibility is to create a hierarchy of classes by deriving classes from a base class that you have defined, and adding methods and data members to specialize the subclasses. Our Animal class and the subclasses derived from it are an example of this. Another possibility is to define a set of classes which are not hierarchical, but which have data members that are themselves class objects. A Zoo class might well have objects of types derived from Animal as members, for instance. You can have class hierarchies that contain data members that are class objects – we already have this with our classes derived from Animal since they have members of type String. The examples so far have been relatively clear-cut as to which approach to choose, but it is not always so evident. Quite often you will have a choice between defining your classes as a hierarchy, and defining classes that have members that are class objects. Which is the best approach to take?

Like all questions of this kind, there are no clear-cut answers. If object-oriented programming was a process that we could specify by a fixed set of rules that you could just follow blindly, we could get the computer to do it. There are some guidelines though, and some contexts in which the answer may be more obvious.

Aside from the desirability of reflecting real-world relationships between types of objects, the need to use polymorphism is a primary reason for using subclasses (or interfaces as we shall see shortly). This is the essence of object-oriented programming. Having a range of related objects that can be treated equivalently can greatly simplify your programs. You have seen how having various kinds of animals specified by classes derived from a common base class Animal allows us to act on different types of animal as though they are the same, producing different results depending on what kind of animal is being dealt with, and all this automatically.

A Classy Example

Many situations involve making judgments about the design of your classes. The way to go may well boil down to a question of personal preference. Let's try to see how the options look in practice by considering a simple example. Suppose we want to define a class PolyLine to represent lines consisting of one or more connected segments, as illustrated in the diagram:

Click To expand

This shows two polylines, one defined by four points, the other defined by seven points.

It seems reasonable to represent points as objects of a class Point. Points are well-defined objects that will occur in the context of all kinds of geometric entities. We have seen a class for points earlier that we put in the package Geometry. Rather than repeat the whole thing, we will define the bare bones we need in this context:

public class Point {
  // Create a point from its coordinates
  public Point(double xVal, double yVal) {
     x = xVal;
     y = yVal;
  }

  // Create a point from another point
  public Point(Point point) {
     x = point.x;
     y = point.y;
  }

  // Convert a point to a string
  public String toString() { 
     return x+","+y; 
  }

  // Coordinates of the point
  protected double x;
  protected double y;
}

Both data members will be inherited in any subclass because they are specified as protected. They are also insulated from interference from outside the package containing the class. The toString() method will allow Point objects to be concatenated to a String object for automatic conversion – in an argument passed to the println() method for example.

The next question you might ask is, "Should I derive the class PolyLine from the class Point?" This has a fairly obvious answer. A polyline is clearly not a kind of point, so it is not logical to derive the class PolyLine from the Point class. This is an elementary demonstration of what is often referred to as the 'is a' test. If you can say that one kind of object 'is a' specialized form of another kind of object, you may have a good case for a derived class (but not always – there may be other reasons not to!). If not, you don't.

The complement to the 'is a' test is the 'has a' test. If one object 'has a' component that is an object of another class, you have a case for a class member. A House object 'has a' door, so a Door variable is likely to be a member of the class House. Our PolyLine class will contain several points, which looks promising, but we should look a little more closely at how we might store them, as there are some options.

Designing the PolyLine Class

With the knowledge we have of Java, an array of Point objects looks like a good candidate to be a member of the class. There are disadvantages though. A common requirement with polylines is to be able to add a segment or two to an existing object. With an array storing the points, we will need to create a new array each time we add a segment, then copy all the points from the old array to the new one. This could be time consuming if we have a PolyLine object with a lot of segments.

We have another option. We could create a linked list of points. In its simplest form, a linked list of objects is an arrangement where each object in the list has a reference to the next object. As long as you have a variable containing the first Point object, you can access all the points in the list, as shown in the following diagram:

Click To expand

This illustrates the basic structure we might have for a linked list of points stored as a PolyLine. The points are stored as members of ListPoint objects. As well as constructors the PolyLine class will need a method to add points, but before we look into that, let's consider the ListPoint class in more detail.

There are at least three approaches you could take to define the ListPoint class, and there are arguments in favor of all three.

  • You could define the ListPoint class with the x and y coordinates stored explicitly. The main argument against this would be that we have already encapsulated the properties of a point in the Point class, so why not use it.

  • You could regard a ListPoint object as something that contains a reference to a Point object, plus members that refer to previous and following ListPoint objects in the list. This is not an unreasonable approach. It is easy to implement and not inconsistent with an intuitive idea of a ListPoint.

  • You could view a ListPoint object as a specialized kind of Point so you would derive the ListPoint class from Point. Whether or not this is reasonable depends on whether you see this as valid. To my mind this is stretching the usual notion of a point somewhat – I would not use this.

The best option looks like the second approach. We could implement the ListPoint class with a data member of type Point, which defines a basic point with its coordinates. A ListPoint object would have an extra data member next of type ListPoint that is intended to contain a reference to the next object in the list. With this arrangement, you can find all the points in a Polyline object by starting with its start member that stores a reference to its first ListPoint object. This contains a reference to the next ListPoint object in its next member, which in turn contains a reference to the next, and so on through to the last ListPoint object. You will know it is the last one because its next member that usually points to the next ListPoint object will be null. Let's try it.

Try It Out – The ListPoint Class

We can define the ListPoint class using the class Point with the code:

public class ListPoint {
  // Constructor 
  public ListPoint(Point point) {
    this.point = point;            // Store point reference
    next = null;                   // Set next ListPoint as null
  }

  // Set the pointer to the next ListPoint
  public void setNext(ListPoint next) {
    this.next = next;              // Store the next ListPoint
  }

  // Get the next point in the list
  public ListPoint getNext() {
    return next;                   // Return the next ListPoint
  }

  // Return String representation
  public String toString() {
    return "(" + point + ")";
  }

   private ListPoint next;         // Refers to next ListPoint in the list
   private Point point;            // The point for this list point
}

How It Works

A ListPoint object is a means of creating a list of Point objects that originate elsewhere so we don't need to worry about duplicating Point objects stored in the list. We can just store the reference to the Point object passed to the constructor in the data member, point. The data member, next, should contain a reference to the next ListPoint in the list, and since that is not defined here, we set next to null.

The setNext() method will enable the next data member to be set for the existing last point in the list, when a new point is added to the list. A reference to the new ListPoint object will be passed as an argument to the method. The getNext() method enables the next point in the list to be determined, so this method is the means by which we can iterate through the entire list.

By implementing the toString() method for the class, we enable the automatic creation of a String representation for a ListPoint object when required. Here we differentiate the String representation of our ListPoint object by enclosing the String representation of point between parentheses.

We could now have a first stab at implementing the PolyLine class.

Try It Out – The PolyLine Class

We can define the PolyLine class to use the ListPoint class as follows:

public class PolyLine {
  // Construct a polyline from an array of points
  public PolyLine(Point[] points) {
    if(points != null) {                           // Make sure there is an array
      // Create a one point list
      start = new ListPoint(points[0]);      // 1st point is the start
      end = start;                                 // as well as the end

      // Now add the other points
      for(int i = 1; i < points.length; i++)
        addPoint(points[i]);
    }
  }

  // Add a Point object to the list
  public void addPoint(Point point) {
    ListPoint newEnd = new ListPoint(point);     // Create a new ListPoint
    if(start == null)
      start = newEnd;                            // Start is same as end
    else
      end.setNext(newEnd);       // Set next variable for old end as new end
    end = newEnd;                                // Store new point as end 
  }

  // String representation of a polyline
  public String toString() {
    StringBuffer str = new StringBuffer("Polyline:");
    ListPoint nextPoint = start;            // Set the 1st point as start
    while(nextPoint != null) {
      str.append(" "+ nextPoint);           // Output the current point
      nextPoint = nextPoint.getNext();      // Make the next point current
    }
    return str.toString();
  }

  private ListPoint start;                  // First ListPoint in the list
  private ListPoint end;                    // Last ListPoint in the list
}

You might want to be able to add a point to the list by specifying a coordinate pair. You could overload the addPoint()method to do this:

  // Add a point to the list
  public void addPoint(double x, double y) {
    addPoint(new Point(x, y));
  }

We just create a new Point object in the expression that is the argument to the other version of addPoint().

You might also want to create a PolyLine object from an array of coordinates. The constructor to do this would be:

  // Construct a polyline from an array of coordinates
   public PolyLine(double[][] coords) {
     if(coords != null) {
       // Create a one point list
       start = new ListPoint(new Point(coords[0][0], coords[0][1]));
                                               // First is start
       end = start;                            // as well as end

       // Now add the other points
       for(int i = 1; i < coords.length ; i++)
         addPoint(coords[i][0], coords[i][1]);
    }
  }

How It Works

The PolyLine class has the data members start and end that we saw in the diagram. These will reference the first and last points of the list, or null if the list is empty. Storing the end point in the list is not essential since we could always find it by going through the list starting with start. However, having a reference to the last point saves a lot of time when we want to add a point to the list. The constructor accepts an array of Point objects and starts the process of assembling the object, by creating a list containing one ListPoint object produced from the first element in the array. It then uses the addPoint() method to add all the remaining points in the array to the list.

Adding a point to the list is deceptively simple. All the addPoint() method does is create a ListPoint object from the Point object passed as an argument, sets the next member of the old end point in the list to refer to the new point and finally stores a reference to the new end point in the member end.

The method toString() will return a string representing the PolyLine object as a list of point coordinates. Note how the next member of the ListPoint objects controls the loop that runs through the list. When the last ListPoint object is reached, the next member will be returned as null, and the while loop will end. We can now give the PolyLine class a whirl.

Try It Out – Using PolyLine Objects

We can create a simple example to illustrate how to use the PolyLine class:

public class TryPolyLine {
  public static void main(String[] args) {
    // Create an array of coordinate pairs
    double[][] coords = { {1., 1.}, {1., 2.}, { 2., 3.},
                          {-3., 5.}, {-5., 1.}, {0., 0.} };

    // Create a polyline from the coordinates and display it
    PolyLine polygon = new PolyLine(coords);
    System.out.println(polygon);
    // Add a point and display the polyline again
    polygon.addPoint(10., 10.);
    System.out.println(polygon);

    // Create Point objects from the coordinate array
    Point[] points = new Point[coords.length];
    for(int i = 0; i < points.length; i++)
    points[i] = new Point(coords[i][0],coords[i][1]);

    // Use the points to create a new polyline and display it
    PolyLine newPoly = new PolyLine(points);
    System.out.println(newPoly);
  }
}

Remember that all three classes, Point, ListPoint, and PolyLine need to be together in the same directory as this class. If you have keyed everything in correctly, the program will output three PolyLine objects.

Polyline: (1.0,1.0) (1.0,2.0) (2.0,3.0) (-3.0,5.0) (-5.0,1.0) (0.0,0.0)
Polyline: (1.0,1.0) (1.0,2.0) (2.0,3.0) (-3.0,5.0) (-5.0,1.0) (0.0,0.0)
                                                               (10.0,10.0)
Polyline: (1.0,1.0) (1.0,2.0) (2.0,3.0) (-3.0,5.0) (-5.0,1.0) (0.0,0.0)

The first and the third lines of output are the same, with the coordinates from the coords array. The second has the extra point (10, 10) at the end.

The PolyLine class works well enough but it doesn't seem very satisfactory. Adding all the code to create and manage a list for what is essentially a geometric entity is not very object-oriented is it? Come to think of it, why are we making a list of points? Apart from the type of the data members of the ListPoint class, there's very little to do with Point objects in its definition, it's all to do with the linking mechanism. We might also have lots of other requirements for lists. If we were implementing an address book for instance, we would want a list of names. A cookery program would need a list of recipes. We might need lists for all kinds of things – maybe even a list of lists! Let's see if we can do better.

Let's put together a more general purpose linked list, and then use it to store polylines as before. You should save this in a new directory, as we will implement it as a whole new example.

A General-Purpose Linked List

The key to implementing a general-purpose linked list is the Object class that we discussed earlier in this chapter. Because the Object class is a superclass of every class, a variable of type Object can be used to store any kind of object. We could re-implement the ListPoint class in the form of a ListItem class. This will represent an element in a linked list that can reference any type of object:

class ListItem {
  // Constructor 
  public ListItem(Object item) {
    this.item = item;        // Store the item
    next = null;             // Set next as end point
  }

  // Return class name & object
  public String toString() {
    return "ListItem " + item ;
  }

  ListItem next;             // Refers to next item in the list
  Object item;               // The item for this ListItem
}

It's basically similar to the ListPoint class except that we have omitted the methods to set and retrieve the next member reference. We will see why we don't need these in a moment. The toString() method assumes that the object referenced by item implements a toString() method. We won't use the toString() method here when we come to exercise the general linked list we are implementing, but it is a good idea to implement the toString() method for your classes anyway. If you do, class objects can always be output using the println() method which is very handy for debugging.

We can now use objects of this class in a definition of a class that will represent a linked list.

Defining a Linked List Class

The mechanics of creating and handling the linked list will be similar to what we had in the PolyLine class, but externally we need to deal in the objects that are stored in the list, not in terms of ListItem objects. In fact, we don't need to have the ListItem class separate from the LinkedList class. We can make it an inner class:

public class LinkedList {
  // Default constructor – creates an empty list
  public LinkedList() {}

  // Constructor to create a list containing one object
  public LinkedList(Object item) {
    if(item != null)
      current=end=start=new ListItem(item);    // item is the start and end
  }

  // Construct a linked list from an array of objects
  public LinkedList(Object[] items) {
    if(items != null) {
      // Add the items to the list

      for(int i = 0; i < items.length; i++)
        addItem(items[i]);
      current = start;
    }
  }

  // Add an item object to the list
  public void addItem(Object item) {
    ListItem newEnd = new ListItem(item); // Create a new ListItem
    if(start == null)                     // Is the list empty?
      start = end = newEnd;               // Yes, so new element is start and end
    else {                                // No, so append new element
      end.next = newEnd;                  // Set next variable for old end
      end = newEnd;                       // Store new item as end 
    }
  }

  // Get the first object in the list
  public Object getFirst() {
    current = start;
    return start == null ? null : start.item;
  }

  // Get the next object in the list
  public Object getNext() {
    if(current != null)
      current = current.next;        // Get the reference to the next item
    return current == null ? null : current.item;
  }

  private ListItem start = null;         // First ListItem in the list
  private ListItem end = null;           // Last ListItem in the list
  private ListItem current = null;       // The current item for iterating
  private class ListItem {
    // Class definition as before
  }
}

This will create a linked list containing any types of objects. The class has data members to track the first and last items in the list, plus the member current, which will be used to iterate through the list. We have three class constructors. The default constructor creates an empty list. There is a constructor to create a list with a single object, and another to create a list from an array of objects. Any list can also be extended by means of the addItem() method. Each of the constructors, apart from the default, sets the current member to the first item in the list, so if the list is not empty this will refer to a valid first item. You can see that since the ListItem class is a member of the LinkedList class, we can refer to its data members directly. This obviates the need for any methods in the ListItem class to get or set its fields. Since it is private it will not be accessible outside the LinkedList class so there is no risk associated with this – as long as we code the LinkedList class correctly of course.

The addItem() method works in much the same way as the addPoint() method did in the PolyLine class. It creates a new ListItem object, and updates the next member of the previous last item to refer to the new one. The complication is the possibility that the list might be empty. The check in the if takes care of this. We take special steps if start holds a null reference.

The getFirst()and getNext() methods are intended to be used together to access all the objects stored in the list. The getFirst() method returns the object stored in the first ListItem object in the list, and sets the current data member to refer to the first ListItem object. After calling the getFirst() method, successive calls to the getNext() method will return subsequent objects stored in the list. The method updates current to refer to the next ListItem object, each time it is called. When the end of the list is reached, getNext() returns null.

Try It Out – Using the General Linked List

We can now define the PolyLine class so that it uses a LinkedList object. All we need to do is to put a LinkedList variable as a class member that we initialize in the class constructors and implement all the other methods we had in the previous version of the class to use the LinkedList object:

public class PolyLine {
  // Construct a polyline from an array of coordinate pairs
  public PolyLine(double[][] coords) {
    Point[] points = new Point[coords.length];  // Array to hold points

    // Create points from the coordinates
    for(int i = 0; i < coords.length ; i++)
      points[i] = new Point(coords[i][0], coords[i][1]);

    // Create the polyline from the array of points
    polyline = new LinkedList(points); 
  }

  // Construct a polyline from an array of points
  public PolyLine(Point[] points) {
    polyline = new LinkedList(points);      // Create the polyline
  }
  
  // Add a Point object to the list
  public void addPoint(Point point) {
    polyline.addItem(point);                // Add the point to the list
  }

  // Add a point from a coordinate pair to the list
  public void addPoint(double x, double y) {
     polyline.addItem(new Point(x, y));     // Add the point to the list
  }

  // String representation of a polyline
  public String toString() {
    StringBuffer str = new StringBuffer("Polyline:");
    Point point = (Point) polyline.getFirst();  
                                            // Set the 1st point as start
    while(point != null) {
      str.append(" ("+ point+ ")");         // Append the current point
      point = (Point)polyline.getNext();    // Make the next point current
    }
    return str.toString();
  }

  private LinkedList polyline;              // The linked list of points
}

You can exercise this using the same code as last time – in the TryPolyLine.java file. Copy this file to the directory for this example.

How It Works

The PolyLine class implements all the methods that we had in the class before, so the main() method in the TryPolyLine class works just the same. Under the covers, the methods in the PolyLine class work a little differently. The work of creating the linked list is now in the constructor for the LinkedList class. All the PolyLine class constructors do is assemble a point array if necessary, and call the LinkedList constructor. Similarly, the addPoint() method creates a Point object from the coordinate pair it receives, and passes it to the addItem() method for the LinkedList object, polyline.

Note that the cast from Point to Object when the addItem() method is called is automatic. A cast from any class type to type Object is always automatic because the class is up the class hierarchy – remember that all classes have Object as a base. In the toString() method, we must insert an explicit cast to store the object returned by the getFirst() or the getNext() method. This cast is down the hierarchy so you must specify the cast explicitly.

You could use a variable of type Object to store the objects returned from getFirst() and getNext(), but this would not be a good idea. You would not need to insert the explicit cast, but you would lose a valuable check on the integrity of the program. You put objects of type Point into the list, so you would expect objects of type Point to be returned. An error in the program somewhere could result in an object of another type being inserted. If the object is not of type Point – due to the said program error for example – the cast to type Point will fail and you will get an exception. A variable of type Object can store anything. If you use this, and something other than a Point object is returned, it would not register at all.

Now that we have gone to the trouble of writing our own general linked list class, you may be wondering why someone hasn't done it already. Well, they have! The java.util package defines a LinkedList class that is much better than ours. Still, putting our own together was good experience, and I hope you found it educational, if not interesting. We will look at the LinkedList class in the java.util in Chapter 12.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor