Skip to main content

Introduction To Naked Objects

July 15, 2003




Unzip! Unsnap! AHHHH!! Our business object is naked.
It is time to strip applications of complex UIs and give users
direct access to the business objects. The concept
is simple: write behaviorally complete business model objects and use
generic views and controllers. Thus, if a business model object supports
a public behavior, then the user has access to that behavior.
So why do we need naked objects?

Chances are you have struggled to truly understand the
Model-View-Controller (MVC) pattern. Why is this? The concepts are easy,
right? The Model contains the core business logic; the View is responsible
for displaying a given model's data; the Controller controls the interactions
between the model and the view, typically through event notification.
Of course, anyone who has worked with Swing knows that the View and Controller
are often combined in the same component. For example, a JTable has a model,
but is also both the view and controller. Business logic can
end up in all three layers, too, which violates the DRY principle (Don't Repeat Yourself).

Our Simple Naked Objects Project

We are going to build a simple address book using the
Naked Objects
framework. An address book works nicely because it fits into the naked objects
philosophy and, of course, it is easy to understand. You may download the
example application
here.
Our first requirement is that our
address book may contain zero or more people. Here is the first
iteration of our naked Person:

package com.briancoyner.naked.addressbook;

import org.nakedobjects.object.AbstractNakedObject;

public class Person extends AbstractNakedObject {

   // You must implement this method, which comes from the base class, before
   // the class compiles. We'll talk more about this in a bit.
  
   public Title title() {
      return null;
   }

}

The Naked Objects framework requires that all naked objects implement
the
NakedObject interface. To keep things simple, we can
extend from
AbstractNakedObject.
The AbstractNakedObject
provides the base functionality that makes up a naked object, allowing
us to focus on the business problem, which is creating a simple
address book.
Any object that is "naked" can be seen by the user.
We will see how to expose objects to an application later in the article.
Now that our Person is naked, we need to add a few attributes and provide a way to modify them. This is
accomplished using strict naming conventions and specific
Naked Objects framework classes. Let's add a few attributes:
first name, last name, and birthdate.

package com.briancoyner.naked.addressbook;

import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.value.Date;
import org.nakedobjects.object.value.TextString;

public class Person extends AbstractNakedObject {

   private final TextString firstName;
   private final TextString lastName;
   private final Date birthdate;


   public Person() {
      firstName = new TextString();
      lastName = new TextString();
      birthdate = new Date();
   }

   public final TextString getFirstName() {
      return firstName;
   }

   public final TextString getLastName() {
      return lastName;
   }

   public final Date getBirthdate() {
      return birthdate;
   }

   public Title title() {
      return null;
   }
}

Naked Objects Attributes

Our Person object now has a first name, last name, and birthdate. This is
plenty for now. You should have noticed that we did not use a
java.lang.String or StringBuffer. Instead, we used an
org.nakedobjects.object.value.TextString.
TextStrings
are mutable objects used by the Naked Objects framework to manipulate
string values and tell the framework to create a text field for the view
(we will see this in a bit).
Also, it is considered good form to mark attributes, whose
reference never changes, as final.

Naked Object Methods

Each mutable attribute requires that you provide a corresponding
"getter" method. The framework locates all "getters"
using reflection, and based on the
return type, builds the correct UI component. The first and last names are
simple text fields, and the birthdate is
a text field with date-parsing behavior.
Labels are generated automatically,
too, by stripping the "get" from each "getter" method
and putting a space between characters that differ in capitalization.
You may have noticed that
Person does not contain
"setter" methods. The reason is simple:
TextStrings
are mutable, so there is no reason to change the instance.
There are times
when a "setter" is appropriate. For example, a person can only
wear one pair of shoes at time. One day they may wear tennis shoes, the next
day, sandals. A "setter", in this example, is needed to change a person's
shoes. In addition to "getters" and "setters", there
are numerous methods that the framework looks for, using reflection.
We will examine a few key methods later in the article.

Our Naked Object Exposed

Naked Object - Person
Diagram 1. Our naked person

We did not code anything GUI-specific. We simply created a business
object, and the Naked Objects framework, using reflection, created the Person
view. Remember that if our object supports it, the user gets it.

Diagram 2 shows a quick view of some of the built-in Naked Object types. For
details on these types beyond what is presented in the examples in this
article, please consult the Naked Objects documentation.

Example Types
Diagram 2. Example types

Unit Testing Naked Objects

Now that we have seen how to create a simple naked object, let's turn our
attention to unit testing. The Naked Objects framework provides a very
slick way to unit test our objects. Here is the start of our test fixture:

package com.briancoyner.naked.addressbook;

import org.nakedobjects.object.NakedClass;
import org.nakedobjects.testing.View;
import org.nakedobjects.testing.NakedTestCase;

import java.util.Calendar;

public class TestPerson extends NakedTestCase {

   /**
   * Yes, you must supply a constructor. Hopefully the next version of the
   * Naked Objects framework will use JUnit 3.8.1.
   */
   public TestPerson(String name) {
      super(name);
   }

   protected void setUp() throws Exception {

      // initialize an object store, otherwise a null pointer exception
      // is thrown when trying to create a new View instance.
      init();
      registerClass(Person.class);

   }
}

This code should look very familiar if you have worked with JUnit.
The only exception is a Naked Object's test fixture extends
NakedTestCase. This is a
base class that extends from
junit.framework.TestCase, and
provides several convenience methods to register objects and set up
an object store. We will talk more about object stores later. Let's
test getting and setting the
Person's attributes.

Testing the Object

public void testPersonAttributes() {
   Person person = new Person();
   person.getFirstName().setValue("Brian");
   person.getLastName().setValue("Coyner");

   // Note that the Naked Object Date starts with 1 (1 = Jan, 12 = Dec).
   // This is different than java.util.Calendar.
   person.getBirthdate().setValue(1900, 9, 22);

   assertEquals("First Name.", "Brian", person.getFirstName().stringValue());
   assertEquals("Last Name.", "Coyner", person.getLastName().stringValue());
   Calendar calendar = Calendar.getInstance();
   calendar.set(1900, Calendar.SEPTEMBER, 22, 0, 0, 0);
   calendar.set(Calendar.MILLISECOND, 0);

   assertEquals("Birthdate.", calendar.getTime(), person.getBirthdate().dateValue());
}

Once again, if you are familiar with how to write tests, then this
test should be fairly straightforward. There are a few things to mention,
though, before we continue. First, remember that our person does not contain
any "setters". The Naked Objects framework only
requires "setters" if an instance of an object can be changed.
TextString objects are mutable, so we simply retrieve
the TextString and change the value. This is somewhat different
than a lot of APIs we are used to. Here, we simply retrieve a
reference to a TextString object that holds the first name,
and change the value.

person.getFirstName().setValue("Brian");

Let's remove the deep chaining to see what is going on.

TextString firstName = person.getFirstName();
firstName.setValue("Brian");

Second, the Naked Object's
Date uses slightly different values for representing months.
The Naked Object's
Date starts months at 1 (1=Jan., 12=Dec.).
The
java.util.Calendar starts months at 0 (0=Jan., 11=Dec.).

Testing the View

One of the most impressive and powerful features of the Naked Objects
framework is the use of Views. Views represent the "graphical"
equivalent of an object, but without the object visible on the screen. This
allows us to test our objects and their interactions without
special scripting or complex GUI-testing frameworks. Here is how we can
test our Person using Naked Object views.

public void testPersonView() {
   String viewName = NakedClass.getNakedClass(Person.class).getPluralName();
   View person = getClassView(viewName).newInstance();


   person.fieldEntry("First Name", "Brian");
   person.fieldEntry("Last Name", "Coyner");
   person.fieldEntry("Birthdate", "1/12/1999");

   person.assertFieldContains("First Name", "Brian");
   person.assertFieldContains("Last Name", "Coyner");

   // The Naked Object's Date object converts the date to this format
   person.assertFieldContains("Birthdate", "Jan 12, 1999");
}

Testing views requires understanding how the framework locates
objects (views) and their fields. The first few lines of this test retrieve the
person view. All views are retrieved using the plural
name. If you were to print out the plural name for our
Person
you would see "Persons". Obviously, we would like to have the
plural name be "People". We will see how to do this at the
end of the article.

To specify field names, you must break apart the
"getter" methods. For example, getFirstName() becomes
"First Name". Simply remove "get" and put a space
between characters that differ in capitalization. A
View also provides various assert methods.
Our test asserts that the given fields contain the correct values.

One Last Feature ...

Remember that every naked object must implement a method that returns
back a title. This method comes from the
AbstractNakedObject base class and looks like this:

public abstract Title title();

And here is how we might implement it:

public Title title() {

   String title = "";

   // stringValue() returns null if the value is not specified.
   if (!firstName.isEmpty()) {
      title += firstName.stringValue();
   }

   if (!lastName.isEmpty()) {
      title += (title.length() > 0) ? " " + lastName.stringValue() :
       lastName.stringValue();
   }

   return new Title(title);

}

Titles show up at the top of each object's window (next to the icon).
You can set a title to anything you like. Just make sure it is
descriptive enough for a user to understand the object.

Person with a title
Diagram 3. Person with a title







Adding Addresses

So far we have looked at a very simple naked object. The next step is
to add one or more addresses to a person. Here is how:

package com.briancoyner.naked.addressbook;

. . .

import org.nakedobjects.object.collection.InternalCollection;

public class Person extends AbstractNakedObject {

   . . .

   private InternalCollection addresses;

   public Person() {

      . . .

      addresses = new InternalCollection(Address.class, this);
   }

   public InternalCollection getAddresses() {
      resolve(addresses);
      return addresses;
   }

   . . .

}

The NakedObjects framework contains an
InternalCollection
object that stores objects of a given type. In our case, we are storing
Address objects. We also must supply the collection with
a parent (the owner), creating a one-to-many relationship.
The "getter" method introduces a new method:
resolve(NakedObject). The resolve method is a
static method on AbstractNakedObject that
ensures that the given object exists and is fully formed and in memory
before it is used. This means that we can construct a
Person and lazy-load
the addresses on the first call to
getAddresses(). We will
talk more about persistent object stores in a bit.

Person with Addresses
Diagram 4. Person with an address

Here is the Address class:

package com.briancoyner.naked.addressbook;

import org.nakedobjects.object.AbstractNakedObject;
import org.nakedobjects.object.Title;
import org.nakedobjects.object.value.TextString;

public class Address extends AbstractNakedObject {

   private final TextString description;
   private final TextString street;
   private final TextString city;
   private final TextString state;
   private final TextString zip;

   public Address() {
      description = new TextString();
      street = new TextString();
      city = new TextString();
      state = new TextString();
      zip = new TextString();
   }

   /**
   * Indicate primary location, lake house, etc.
   */
   public final TextString getDescription() {
      return description;
   }

   public final TextString getStreet() {
      return street;
   }

   public final TextString getCity() {
      return city;
   }

   public final TextString getState() {
      return state;
   }

   public final TextString getZip() {
      return zip;
   }

   public Title title() {
      String title = street.isEmpty() ? "" : street.stringValue();

      if (!description.isEmpty()) {
         title += (title.length() == 0 ? "(" : " (") +
            description.stringValue() + ")";
      }

      return new Title(title);
   }
}

Address Object
Diagram 5. Our simple address

There is nothing new in the
Address class that we have not
already seen, and testing
this class is simple, too, especially using views. Of course, the
integration between a
Person and multiple addresses is
worthy of more detail.

Adding an Address to a Person

  1. Create a new Address object

  2. Drag and drop the
    Address object on a
    Person's
    "Addresses" field.

    You should see the field turn green, indicating
    it is okay to drop the object. The framework
    takes care of validating if an object can be dropped.
    (Hint: look at the
    InternalCollection
    object.) The field turns red if you cannot drop the object.

Person With Address
Diagram 6. Our person has a home

If you want to see all of the attributes from the
Person view,
simply double-click the "Addresses" field.

Person With Address
Diagram 7. Address expanded

To remove an address, simply right-click on the
Person's
"Addresses" field and select "Remove reference".

Remove Address Object
Diagram 8. Removing an address

Testing One-to-Many Relationships

We have seen how to write and test simple naked objects. We have
also seen how to associate objects by dragging and dropping. Now let's
look at how we can test this behavior. Here is a simple test:

protected void setUp() throws Exception {
   init();
   registerClass(Person.class);

   // Don't forget to register the class registerClass(Address.class);
}

public void testAddAddressToPerson() {
   String viewName = NakedClass.getNakedClass(Person.class).getPluralName();
   View person = getClassView(viewName).newInstance();

   viewName = NakedClass.getNakedClass(Address.class).getPluralName();
   View address = getClassView(viewName).newInstance();

   person.drop("Addresses", address.drag());
   person.assertFieldContains("Addresses", address);
}

Creating a Runnable Application

So far we have seen how to create and test naked objects. Now let's delve
into creating a Naked Objects application. Here is the code
to launch our address book:

package com.briancoyner.naked.addressbook;

import org.nakedobjects.Exploration;
import org.nakedobjects.object.NakedClassList;
import org.nakedobjects.object.NakedObjectStore;
import org.nakedobjects.object.TransientObjectStore;

public class AddressBookApp extends Exploration {

   public void classSet(NakedClassList classes) {
      classes.addClass(Person.class);
      classes.addClass(Address.class);
   }


   public static void main(String[] args) {
      new AddressBookApp();
   }
}

When developing a Naked Objects application, you should extend from
Exploration, which is a template for prototyping. An
Exploration contains numerous default configurations. One
piece that you must supply, though, is the objects to expose. This is done by
implementing the abstract method
classSet(NakedClassList).
This method tells the framework which objects to make available to the users.
Once the application stabilizes, you may move the application's configuration
from the
Exploration object to a configuration file. This
allows for complete customization without have to recompile.

Here is a screen shot showing the classes available to user:

Application classes
Diagram 9. Classes exposed to the application

To create a new person, right-click on a class and select "New Person..."

Creating a new person
Diagram 10. Creating a new person

Object Stores

The framework supports multiple types of object stores. Here is a list of
object stores readily available:

  • XML Object Store

    Stores each naked object instance in a separate XML file. You
    should only use this when you are dealing with a small number of objects.
    The XML files are placed in the current working directory:
    ~/xml/*.xml.

  • Serialized Object Store

    Stores each naked object instance in a separate file, using serialization.
    You should only use this when you are dealing with a small number of objects.
    This is typically faster than using XML files.

  • SQL Object Store

    Uses JDBC to store naked objects to a persistent
    data store (MySQL, Oracle, etc.).

  • EJB Object Store

    Allows naked objects to use EJBs.

  • Transient Object Store

    This is the default object store, and all objects are stored
    in memory and are lost when the application quits.

To facilitate rapid prototyping, the framework defaults to a transient store.
As the application stabilizes, it is appropriate to plug in a persistent
object store, such as XML or a database. We can change the object store
by overriding the installObjectStore() method in our
AddressBookApp.
Here is how we hook up an XML file-based object store:

protected NakedObjectStore installObjectStore() {
   return new XmlObjectStore();
}

The complexity of a Naked Objects application grows when
dealing with more complex persistence capabilities. For more information on
creating a robust, persistence-aware application, consult the Naked Objects
documentation.

Other Goodies

The Naked Objects framework relies heavily on reflection, as we have seen
throughout this article. Don't be scared, because there is plenty
of documentation to get you started. You can start by reading the
online book, which
also contains great chapter on object-oriented programming.
Listed below are a few treasures that you'll uncover from the online book.

Changing the Plural Name

By default, the framework adds an "s" to the classname to create the plural
name. Irregular nouns can be changed by implementing this method:

public static String pluralName();

Here is how we change "Persons" to "People"

public static String pluralName() {
   return "People";
}

Ordering Objects

How objects are ordered is specific to how
ClassLoaders parse
.class files. Therefore, we need a way to specify the ordering
of objects on the screen. Luckily,
the solution is easy. Simply add this method signature to your naked object:

public static String fieldOrder();

Here is how we might order our objects:

public static String fieldOrder() {
   return "First Name, Last Name, Birthdate, Addresses";
}

Changing Icons

Icons are simple to add to your application. Simply create an
images
directory that contains your images and place it on the
CLASSPATH.
Of course, you will need to follow a few naming conventions. We need two
image files for each object (16 and 32 pixels). The 32-pixel image is
used for the class view and the 16-pixel image is used for an instance view.
For example, our
Person needs the following files:

  • images/Person16.gif
  • images/Person32.gif

Summary

Naked objects provide an interesting way to think about developing software.
In this article, we built and tested a simple application using the open source
Naked Objects framework.
Overall, this framework offers a substantial amount of flexibility and structure when
developing GUI applications, and we barely scratched the surface with what
this framework can do. One of the most powerful features of this framework
is its use of views, which allow us to test our objects and their interactions much
like an end user might use them. Finally, even if you never build a Naked Object system,
hopefully you will start thinking in terms of writing behaviorally complete
objects.

Brian Coyner is coauthor of the Java Extreme Programming Cookbook and a Senior Software Engineer with Object Computing, Inc. in St. Louis, MO.
Related Topics >> Programming   |