Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Effective object-oriented design

Revisit what Tony means by effective OO design

  • Print
  • Feedback

I've received quite a bit of feedback on my "Singletons Rule" Q&A. Unfortunately, much of the confusion seems to stem from this sentence:

The difference between using a singleton over a class with static methods boils down to effective object-oriented design.


In that response, I did not adequately describe what I consider "effective object-oriented design." For that oversight, I do apologize. Sometimes I take too much for granted.

In fact, you may have read the emails a reader and I swapped in the February 23 "Letters to the Editor" (scroll down to the "Singletons Rule" heading).

Considering all this, what is effective object-oriented design?

One important facet of effective object-oriented design is encapsulation. Encapsulation hides your classes' inner details from the outside world. Moreover, it protects your objects from improper use by forcing all use to go through the objects' interfaces. Encapsulation also protects objects that use the encapsulated object. If not for encapsulation, the users of your object could become dependent upon the object's implementation. This tight coupling between objects means that the users of your object would break each time you needed to rework the implementation.

Static classes provide encapsulation.

Let's look at a class that has only static methods:

public class TraceClass {
      private static boolean debug;
      public static void writeDebug( boolean d ) {
            debug = d;
      }
      public static void debug( String message ) {
            if( debug ) { // only trace if debug is true
                  System.out.println( "DEBUG: " + message );
            }
      }
      public static void error( String message ) {
            System.out.println( "ERROR: " + message );
      }
      protected TraceClass() {} // do not allow instantiation
}


The TraceClass writes trace messages to the command line. The TraceClass keeps an internally encapsulated state: debug. All interaction with TraceClass must be done through the interface. Indeed, we have encapsulation.

However, there are two other facets to effective object-oriented design: inheritance and polymorphism.

You can write the following:

public class ChildTraceClass extends TraceClass {
      private static boolean error;
      public static void writeError( boolean e ) {
            error = e;
      }
      public static void error( String message ) {
            if( error ) {
                  System.out.println( "ERROR: " + message );
            }
      }
      public static void fatal( String message ) {
            System.out.println( "FATAL: " + message );
      }
      protected ChildTraceClass() {} // do not allow instantiation
}


So you can get a piece of what the inheritance offers. However, there is more to inheritance than simple implementation reuse. Inheritance also allows you to program by difference, and allows for type substitution.

The example above shows inheritance for difference. However, it is impossible to substitute ChildTraceClass for TraceClass. You cannot pass a class as argument to a method or assign it to a variable. You can't register a static as a listener. You can't say method( TraceClass ). This limitation makes type substitutability impossible and limits how you can use a class that only has static methods.

  • Print
  • Feedback

Resources