Comp201: Principles of Object-Oriented Programming I
Spring 2008 -- Lab04: Static vs. non-static; Singleton Pattern; Strategy Pattern     


You will need to use Full Java language level for this lab.

Static vs. non-static; Singleton Pattern

This lab will cover:

Resizable Rectangle

Examine how the  window of the browser that is displaying these lab notes behaves: it is capable of changing its width and height!  Let us try to model such a window with a class called ResizableRect.

To start with, we can say that ResizableRect is an IShape that has a width and a height.  So the code looks like the following.

/**
 * Represents a geometric shape that can compute its own area.
 */
interface IShape {

    double getArea();

}
/**
 * Represents the set of rectangles whose width and height can be modified dynamically
 * via appropriate "settors" methods.
 * @author DXN
 */
public class ResizableRect implements IShape {

    private double _width;
    private double _height;

    /**
     * Initializes the width and height of this ResizableRect to the given parameter values.
     * @param w the value for the width of this ResizableRect.
     * @param h the value for the height of this ResizableRect.
     */
    public  ResizableRect(double w, double h) {
        _width = w;
        _height = h;
    }

     /**
     * @return the area of this ResizableRect.
     */
    public double getArea() {
        return _width * _height;
    }

}

TThere is not a whole lot you can do with the above code besides instantiating a bunch of ResizableRect objects, each with its own width and height, and computing their areas;

Interactive Exercise 1

Copy and paste the above code into the DrJava Definitions pane, save and compile.

In the Interactions pane, you can enter for examples:

Now enter:

What you see is called the default String representation of r1 and r2.  Recall in Java, all classes are subclasses of the class Object.  The class Object comes with a method called toString() that returns a String representation that consists of the name of the class followed by some cryptic hexadecimal code.  If you want to have a more descriptive String representation of the ResizableRect objects, you will need to override the toString() method that ResizableRect inherits from the class Object

Interactive Exercise 2

Add the following method to ResizableRect.

    /**
     * @return the String "ResizableRect(width, height)", where width and height are
     * the actual values of _width and _height respectively.
     */

    public String toString() {
        return "ResizableRect(" + _width + ", " + _height + ")";
    }

    

Save and compile.

Now re-run interactive exercise 1.  Better String representation!

How can we change the width and height of a ResizableRect object?

Setter Method

To allow code external to a class to modify its private fields, we add what is called "settor methods".  In our example of class ResizableRect, we need two setters: one for the width and one for the height.

Add the following methods to the class ResizableRect.

    /**
     * Sets the width of this ResizableRect to the given parameter value.
     * @param w the new value for the width of this ResizableRect.
     */
    public void setWidth(double w) {
        _width = w;
    }

   
    /**
     * Sets the height of this ResizableRect to the given parameter value.
     * @param h the new value for the height of this ResizableRect.
     */
    public void setHeight(double h) {
        _height = h;
    }

    

Interactive Exercise 3

Re-run interactive exercise 1.

Enter a few calls to change the width and the height of r1 and r2 and display r1 and r2.  Does changing the size of r1 affect r2 at all?  This bring up the following point.

Instance Variable:

Each instance of ResizableRect possesses its own  _width and _height fields.  Changes made to these fields in one ResizableRect object do not affect  in any way the same fields in another ResizableRect object.  Such fields in OO parlance are called instance variables as they are owned by the individual instances of a class and are not shared among the different instances.

Now enter the following assignment and computations:

What has happened to r1? Why?

Object Reference

In the above assignment, r3 is said to reference the same object as r1.  When we enter r3.setWidth(19), we are telling the ResizableRect object that r3 is referencing to change its width to 19.  Since r1 is referencing the same object as r3, r1 should reflect the same change.  Here is a picture depicting the notion of variables referencing objects.

Questions: 

Garbage Collection:

When an object is no longer referenced by anything in a Java program, it is considered as "garbage" and is eventually "collected" and returned to the "heap" of memory available for use by the rest of the current program.  Garbage collection is done automatically and transparently (i.e. "behind-the-scene" unbeknownst to the program code).  At this level of programming, we do not have to concern ourselves with how and when garbage collection is carried out.

Interactive Exercise 4

Add the following line of code to the class ResizableRect (preferred at the top of the class definition):


    public static int NUM_SIDES = 4;  // The number of sides in all rectangles.

    

Save and recompile.  Now enter the following:

Observe that there are two ways to access the field NUM_SIDES.  One way is via the object (i.e. r1 or r2).  The other way is via the class name ResizableRect.  When you define a field as static, you do not need an object to reference it.  

Now enter:

What happens to NUM_SIDES?  

Class Variable

As a static field, unlike non-static field (aka instance variables), NUM_SIDES is shared by ALL instances of ResizableRect.  In OO parlance, a static field is called a class variable.  A class variable (i.e. static field) is said to have global class scope, that is it can be accessed by ALL instances of the class.

When do we use class variables (aka static fields in Java)? We use a class variable to represent a property that is intrinsically a property or characteristic of the class as a whole, that is something common to ALL instances of the class.  For example, the number of sides in a rectangle is a property that is characteristic of the class of all rectangles and thus is best defined as "static ".

final Fields and Methods

As you can see from the above, we can access NUM_SIDES from outside of the class and modify its value.  This is obviously a bad idea.  To make a field unmodifiable, we add the key work final  to its definition.  In Java, to predefine constants, the common idiom is to use public static final fields.  Remember Math.PI?  That's a public static final field in the Math class.

Add the key word final  to the definition of NUM_SIDES as shown below:

    public static final  int NUM_SIDES = 4;  // The number of sides in all rectangles.

Repeat interactive exercise 4 above and see to it that NUM_SIDES cannot be modified.

We can also add the key word final  to the definition of concrete methods.  This will prevent any subclass to override the method.  At this point we have not seen the use of final methods yet.  We will encounter final methods in latter lectures.

Singleton Pattern

The class Randomizer that was given to you in the BallWorld program uses all static methods to carry out all computations.  Analogous to static fields, a static method has global class scope and is shared by all instances of the class.  It makes sense for Randomizer to use static methods since it does not have any fields and as a result all instances of Randomizer look alike and behave in the same way.  However, using static methods  is not a very flexible way to define behaviors for objects.  What we really want to do is to have a way to ensure that there is only one instance of Randomizer that can be shared by all possible Java program.  Having one single object of a class (whenever appropriate) helps save execution time and memory space.  To achieve such uniqueness result, we use a coding technique called the Singleton design pattern.  Here is the coding pattern for the Singleton design pattern.

class ClassName {
  public static final ClassName Singleton = new ClassName();
  private ClassName() { };
  // other fields and methods...
}

In the above, by making the constructor private, no code outside the given class can call new to instantiate an object of the class.  The only way to obtain an object of the class is to reference ClassName.Singleton.  Note that because ClassName.Singleton is final, no code can re-assign it to anything else.

One of the upcoming homework problems is to convert the Randomizer class into a singleton.  We shall do it in two steps.

  1. Define an interface, IRandomizer, that contains all the methods of Randomizer, none of which should be static.  Try to define a static method in an interface and see what happens!
  2. Define Randomizer to implement IRandomizer using the Singleton pattern

With this setup, we can define distinct implementations of IRandomizer and use them in different situations

 


Last Revised

©2008 Stephen Wong and Dung Nguyen