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

The canonical object idiom

Defining a baseline set of functionality for objects

  • Print
  • Feedback
In last month's installment of Design Techniques, I proposed the "event generator" as a Java idiom. This month I will propose another idiom, which I am calling the "canonical object."

I call this object idiom "canonical" because it represents the simplest form an object should take. The idea behind this idiom is to suggest a baseline functionality that you give by default to any object you design. You may adopt this idiom in your own programming practice, or, if you are developing a set of style guidelines for a team or organization, you may want to make the idiom part of those guidelines. The intent of the idiom is not to force programmers to endow every object with this baseline functionality, but simply to define a default functionality for every object. In other words, programmers would be encouraged to make a new object canonical unless they have specific reasons to depart from the idiom in a given, specific case.

A fundamental tenet of object-oriented programming is that you can treat an instance of a subclass as if it were an instance of a superclass. Because all classes in Java are subclasses of java.lang.Object, it follows that another basic tenet of Java programming is that you can treat any object as, well, an Object.

As a Java programmer, you can treat an object as an Object in many ways. When you invoke any of the methods declared in class java.lang.Object on an object, for example, you are treating that object as an Object. These methods, some of which are clone(), equals(), toString(), wait(), notify(), finalize(), getClass(), and hashCode(), provide basic services commonly exercised on Java objects of all kinds. In addition, you may find yourself wanting to serialize just about any kind of object. All of these activities are ways you may treat objects of many diverse classes simply and uniformly as Objects.

Taken together, all the activities that are commonly performed on Java objects constitute a set of services that, in most cases, should be built into every class you design. The question raised by this article and answered by the idiom is: What do you need to do, at a minimum, to make each object you define support the services commonly expected of all objects?

This idiom defines a baseline set of requirements for object definitions and names objects that implement the baseline "canonical objects."

Recipe: How to concoct a canonical object

You can turn instances of any class into canonical objects by taking the following steps with the class:

  1. Implement Cloneable (unless a superclass already implements it or the object is immutable).

  2. If the class includes instance variables that may at some point in the lifetime of its instances hold references to mutable objects, override clone().

  3. Override equals().

  4. Implement Serializable (unless a superclass already implements it).


Example code

Here's a Java class that illustrates the canonical object idiom:

// In file canonical/ex1/Worker.java
import java.io.Serializable;
import java.util.Vector;

public class Worker implements Cloneable, Serializable {
private String name; private Vector doList;
public Worker(String name, Vector doList) { if (name == null || doList == null) { throw new IllegalArgumentException(); } this.name = name; this.doList = doList; }
public Worker(String name) { this(name, new Vector()); }
public void setName(String name) { if (name == null) { throw new IllegalArgumentException(); } this.name = name; }
public void addtoList(Object job) { doList.addElement(job); }
public Object clone() {
// Do the basic clone Worker theClone = null; try { theClone = (Worker) super.clone(); } catch (CloneNotSupportedException e) { // Should never happen throw new InternalError(e.toString()); }
// Clone mutable members theClone.doList = (Vector) doList.clone(); return theClone; }
public boolean equals(Object o) {
if (o == null) { return false; }
Worker w; try { w = (Worker) o; } catch (ClassCastException e) { return false; }
if (name.equals(w.name) && doList.equals(w.doList)) { return true; } return false; }
//... }


In the code listing above, instances of class Worker are canonical objects because the Worker objects are ready for (1) cloning, (2) serialization, and (3) semantic comparison with equals. To make Worker objects ready for cloning, class Worker implements Cloneable. Implementing Cloneable is necessary in this case because Worker objects are mutable and Cloneable isn't implemented by any superclass. Likewise, to make Worker objects ready for serialization, class Worker implements Serializable. Because no superclass of Worker implements Serializable, class Worker itself must implement it. Lastly, class Worker, like any other class with canonical instances are canonical objects, overrides equals() with a method that does an appropriate semantic comparison of the two objects.

  • Print
  • Feedback

Resources
  • Bill Venners' next book is Flexible Java http://www.artima.com/flexiblejava/index.html
  • Bill Venners is currently riding his bike through various European countries. As he encounters Internet cafes, he'll be posting reports about his trip. Follow along at
    http://www.artima.com/bv/travel/bike98.html
  • The discussion forum devoted to the material presented in this article http://www.artima.com/flexiblejava/fjf/canonical/index.html
  • Links to all previous design techniques articles http://www.artima.com/designtechniques/index.html
  • Recommended books on Java design, including information by the Gamma, et al., Design Patterns book http://www.artima.com/designtechniques/booklist.html
  • A transcript of an e-mail debate between Bill Venners, Mark Johnson (JavaWorld's JavaBeans columnist) and Mark Balbe on whether or not all objects should be made into beans http://www.artima.com/flexiblejava/comments/beandebate.html
  • Source packet that contains the example code used in this article http://www.artima.com/flexiblejava/code.html
  • Object orientation FAQ http://www.cyberdyne-object-sys.com/oofaq/
  • 7237 Links on Object Orientation http://www.rhein-neckar.de/~cetus/software.html
  • The Object-Oriented Page http://www.well.com/user/ritchie/oo.html
  • Collection of information on OO approach http://arkhp1.kek.jp:80/managers/computing/activities/OO_CollectInfor/OO_CollectInfo.html
  • Design Patterns Home Page http://hillside.net/patterns/patterns.html
  • A Comparison of OOA and OOD Methods http://www.iconcomp.com/papers/comp/comp_1.html
  • Object-Oriented Analysis and Design MethodsA Comparative Review http://wwwis.cs.utwente.nl:8080/dmrg/OODOC/oodoc/oo.html
  • Patterns discussion FAQ http://gee.cs.oswego.edu/dl/pd-FAQ/pd-FAQ.html
  • Patterns in Java AWT http://mordor.cs.hut.fi/tik-76.278/group6/awtpat.html
  • Software Technology's Design Patterns Page http://www.sw-technologies.com/dpattern/
  • Previous Design Techniques articles http://www.javaworld.com/topicalindex/jw-ti-techniques.html