To understand how Naked Objects works we are going to look at a simple application, a reservation system for limousine company: Executive Car Services (ECS). We shall be looking at the system from the perspective of both an end-user (in this case a call-centre booking agent) and a software developer. For the developer perspective we shall be looking at the system using a popular IDE (VisualAge). We shall not be looking at the actual Java code - but at how the structure of the software written by the developer relates to the system as seen by the user. Latter documents look at the actual coding in the form of a tutorial.
Below is a basic screenshot from the ECS system, as seen by a
reservations agent. On the left hand side there is a window that lists
the classes of business object available to this user:
Bookings
, Cities
and so on.
Turning to the developer's view of the project, we can see a set of Java projects. 'ECS Bookings' contains all the code for the Executive Car Services project, in three packages. The 'ecs.explore' package contains the prototype code written during the exploratory phase; 'ecs.delivery' contains the code written for the delivered system; and 'ecs.tests' contains both the unit tests and user-acceptance tests that support the development of the delivery code.
Inside the 'delivery' package package we can see a set of Java class definitions that correspond to those shown to the user.
The business system consists of nothing more than these 'naked'
object definitions. There is no 'application' code sitting on top
of these objects. In the list you can see a TestApp
class, which is used by the developer to test the application, but which
contains no business logic.
Back in the user's view we have now right-clicked on the
Locations icon, which has brought up the menu of 'class
methods' that are available to the user. These include the ability to
create a new instance of this class and to retreive existing ones
from storage. These methods are generic and automatically provided by the
framework for any naked object class - the programmer does not have to
write any code for this. It is possible to add class methods specific to
a particular business class, for example, a method on the
Booking
class to generate an analysis of bookings over the
next month . Equally it is possible to inhibit the generic
methods.
In the user's view each class is represented by its own icon. The
framework looks for a .gif
file (within the project's
images
directory) with a file name corresponding to
the class name. If the programmer does not provide one then a default
icon will be displayed.
The title shown under each of the class icons is automatically
derived from the name of the Java class, with some reformatting. Thus,
the Booking
class is shown as Bookings
and CreditCard
as Credit Cards . (If the
class name is takes an irregular plural such as 'cities' or 'fish'
then the programmer can specify this manually.)
On the user's screen we can now also see a number of icons representing individual instances of several of the business classes, plus one 'collection' of instances (the seven cities).
Each instance has an icon to indicate which class it belongs to, and a title (e.g. Richard Pawson or Logan Airport ) to indicate what that specific instance represents. Although each object uses the same icon as its class by default, the programmer can also over-ride this, when there is good reason to do so. It would be possible, for example, to have the icon for an Employee to be a photograph of that individual employee. A more common use is to have a small number of variaints on the basic icon, to provide a visual indication of that object's status - such as a red, amber, or green background.
Notice that the New York icon appears in more than one place (i.e inside a Cities collection and on its own). These two icons are both views of the same object. What you can do through one icon, you can do through the other.
Turning to the developer's view, each class has a
title
method defined (the screen shows it for the
City
object).
Normally the title is built from one or more of the object's
attributes such as the name or date. (Note: In addition to
showing the name of the method (title
), our IDE
shows that the method will return an object of class
Title
.)
Right-clicking on one of the instances produces a pop-up menu. The actions at the top of this menu are generic - they are automatically generated by the framework for any naked object class. The programmer does not need to write any code for these. (We'll look at the object-specific actions at the bottom of the menu under actions ). The generic methods include the ability to view the object in a variety of different ways. Double-clicking on an object is a short-cut to the main view.
Double-clicking on the Booking instance reveals a set of labelled fields, each containing some text or an icon representing another object.
Turning to the developer's view, we can see that the fields seen by
the user correspond directly to the get
methods defined
within the Booking
class.
Each declared get
method has two parts. The second
part gives the method a name that programmers can use to make requests of
this object (e.g. getContactTelephone
). In the user
view the name of each field is just a reformatted version of the
get
method name (e.g. Contact
Telephone ). This correspondence improves communication
between the user and developer. If necessary, this automatic
correspondence can be over-ridden, for example to display the field name
in another language.
The first part of the method declaration defines the type of object that the field will contain, and we can recognise three kinds of construct here.
TextString
, Date
,
WholeNumber
and Currency
. The first two
of these correspond closely to the standard Java classes of
String
and Calendar
, and indeed
delegate most of the functionality to those classes. The third
encapsulates one of Java's primitive types, and the last is
novel in that it combines two types so that an amount as well as
a currency type is held. The reason for introducing these new types,
specific to the Naked Objects framework, is that they allow these value
objects to be exposed directly to the user. The programmer could add
other classes of naked value-object specific to a business need, for
example Temperature, or Length. (It is considered good object
programming practice to encapsulate a value with its unit of
measurement e.g. Metres). From the user's perspective these
value-objects fields show up as formatted text. [In a future release we
expect to change the value objects to have user-invokable actions also.
For example, a Date
field might have a pop-up calendar
from which a date can be selected, and a
Currency
field might have a pop-up convertor]InternalCollection
object,
supplied with the framework. In the user view we can see that the
field displays a list of objects under that field, with the grey
blob at the bottom of the list to indicate that it is possible
to add more.The get
methods (formally called 'accessor' methods)
specify the data that can be requested of it (the data, in this case,
almost always takes the form of other object types). Additionally,
set
specify the data that can be passed to an object.
Using get
and set
methods (which is
standard Java practice) means that whatever is accessing the data from
outside the object does not need to know how that data is stored - for
example it may be that the object doesn't store that data at all, but
creates it dynamically each time it is asked for. Additionally, the
set
method will typically contain code to check that
the data being passed is valid, and to ensure that the object receiving
that data always remains in a valid data.
In the Naked Objects framework, these get
and
set
methods determine the set of fields that can
potentially be viewed and altered by the user.
Looking at the developer's view of the Booking
object, we can see several set
and get
methods.
For example there is a getDropOff
method and a
setDropOff
method, which are the two accessor methods
for the dropOff
variable. Both methods deal
with the same type of object (a Location
). The
set
method requires a Location
to be passed in, returning nothing ( void
). The
get
method does not require any parameters,
but itself returns a Location
.
To understand how these are used we need to look again at the three main uses of fields.
For business object fields there will be both a
get and a set method. e.g. getCity
and
setCity(City)
. (In this case the name for the
field 'City' happens to be same as the name of the class of object it
can contain, but that is often not the case, as we saw for the
Pick Up
and Drop Off
fields
which both contain Location
objects). If the
Run Time Environment sees a get
and
set
method with the same name and a business object
type, then it will display a reference field that will either show an
object of that type, if the field is set, or the grey blob with the
name of the allowed type if it is empty.
Moreover, the simple provision of these accessor methods
is sufficient to enable the viewing mechanism to provide drag and
drop - the programmer doesn't have to write any additional code
for this. If you drag an object over a field and that object
matches the type specifiied in the set
method,
then the field (both the grey blob and the label) will flash
momentarily green.
Let go of the mouse button and the icon representing that object will appear in the field. (In technical terms the system has created a new pointer to the dragged object inside the field):
If you drag an object of the wrong type over a field
then the field will momentarily flash red and you will not be
able to drop it there. For example the Seattle
object cannot be dropped into the Pick
Up
field as it expects a Location object and not a City.
get
method is required in order to display the value in the field. When
the object is created it places a value object in the field.
The value (e.g. the date) being held by this object may be
changed, but the object itself can never be replaced - that's
why we don't need a set
method. The methods
for changing the value in the value object are provided by the value
object itself. The user is not aware of all this indirection: all
he/she sees is a field that they can type into. (We'll cover
how to prevent the user from altering a field under 'About' methods . ) get
method for similar reasons. When the object is
first set up a new instance of
InternalCollection
is created in that field. The
get
method is needed so that the viewing mechanism (or
any other object that needs to) can access the collection. There
is no need for a set
method, because that
InternalCollection
object should never be replaced.
When the users adds business objects into the field (by drag and drop),
the viewing mechanism adds them to
InternalCollection
itself. Selecting Remove
Reference from the element's popup menu causes the viewing
mechanism to remove the selected object from collection. As these simple get
and set
accessors follow a standard Java pattern, some IDEs (including
VisualAge) will generate them automatically from the declaration of a
variable. These standard pieces of code need only be extended if you
wish to add some data validation, formatting or other constraints into
the methods.
It is also worth stressing here that although we have explained
how the get
and set
methods trigger
the naked object viewing mechanism to provide user capabilities such as
drag and drop, these accessors are no different to the ones you would
have to write for a conventional business object that did not show
through to the user.
Association
We have already seen how a
field with the standard accessor methods can be used to implement a
simple association between two objects: a Location
object
knowing in which City
it belongs, for example.
Oftentimes it is necessary to implement more sophisticated
associations. The most common of these is when you want an association
to be navigable from both directions. For example, when you open a
Booking
object you expect to see the
Customer
it is associated with. But it might also be
very useful to have a field in the Customer
containing
a collection of all the recent Bookings
made by the
Customer
. We therefore need to ensure that every time a
Customer
is associated with a Booking
, then
that Booking
becomes associated with the
Customer
.
This is achieved through the use of an
'associate
' method, and a corresponding
'dissociate
' method, as we can see here:
the Booking
object has a method to associate a
Customer
. Were we to examine the Java code we would see
that the first thing this method does is call the
setCustomer
method. Then it calls a method on the
customer object to add itself (the booking) to the collection of bookings
in that object. The principle here is that for any association between
two objects that needs to be navigable in both directions, one of the
objects is always responsible for maintaining that association and the
other one delegates the task to it.
As with the simple accessors, the
associate
/ dissociate
methods are not
specifically for use by the user - they can be called by any other
object or program that needs to form an association. However,
when the user drags and drops an object onto a field in another object,
the viewing mechanism will automatically check to see if there is an
associate
method and call that. If there
is no associate
method it will call the
simple set
method.
Readers may be asking why this responsibility for maintaining
the bi-directional associations cannot simply be included in the
set
methods. The reason is that the
set
method is called by the persistence mechanism
whenever an object is retrieved from persistence. Effectively that
object is re-created using the data provided by the persistor, and
fields are individually set. You only want the association code
called when the association is first created.
In systems designed using naked objects, all business behaviours are
implemented as methods on the core business objects themselves: there is
no 'process' layer or other form of scripting that sits on top of the
objects. If we right click on one of our business objects such as a
Booking
:
we see that the pop-up menu includes a number of
object-specific business behaviours: in this case Check
Availability , Return Booking and
Confirm. As with the fields, there is a 1:1 correspondence
between these menu-actions and the methods in the Java code.
Turning to the developer view of Booking
:
We can see that there are three methods corresponding to
these menu items. In all three cases the method name is prefixed
by action
(e.g. actionConfirm()
). The framework's viewing mechanism uses this action prefix as a cue to
make that method available to the user, stripping off the prefix
and separating the words. (As with the attributes and
associations, it is possible to make the method name different
from the user menu name, for example to support multiple language.)
However, the action prefix should not be thought of as specifically
having to do with the user interface: the action methods can all
be called by other objects. Think of action
as merely
a convention for writing business behaviours.
Any method on the Java object not preceded by
action
will not be available directly to the
user. Such methods could only be invoked by other objects. We
generally only use this to implement technical behaviours rather
than business behaviours - all of which should be designed as though a
user was invoking them directly, even if, in fact, they are most
often called by other objects.
The actionReturnBooking()
method is preceded by
Booking
, indicating that this method will return a
Booking
object. Were we to examine the Java code we
would see that this method creates a brand new
Booking
object, copies across the
Customer
, Payment Method
and
Contact Telephone
from the existing booking,
and copies the Pick Up
and Drop
Off
Locations, but swaps them over. If this method was
called by another object in the system, then this new booking would be
returned to that object, which would be expecting to do something with
it. If, however, the method had been called by the user selecting
Return Booking... on the pop-up menu, then the returned
object would be returned to the viewing mechanism and appear as a new
window on the user's screen. (The ellipsis (...) on the menu option is
an indicator to the user that this action will return something, and is,
again, auto-generated by the viewing mechanism).
The other two action methods,
CheckAvailability
and Confirm
, are both
preceded by void
indicating that they do not
return an object. The result of these methods will usually be a
change to the state of the object (meaning a change to one or more
of the fields) - although they could also invoke methods on
other associated objects.
Now lets look at a slightly different kind of action method, this time on the Location object. Looking first at the developer's view:
we can see a method called actionNewBooking
. This
method takes a Location
object as a parameter, meaning
that when the method is called on a particular Location
object, another Location
object must be supplied as an
argument. (The previous three examples of actionMethods were all
'zero parameter' methods). The purpose of this particular method is
to allow a new booking to be created directly from two
Location
objects, representing the pick-up and drop-off
locations.
The user invokes this method simply by dragging one Location object
onto another. Usually this would be done within a list of Locations:
either a list of the most popular Locations
within a
City
(such as airports and theatres), or
Locations
that are specific to an individual customer such as
their home and office.
The above methods we have just looked at should not always be available. For example, in the Booking shown above, the Confirm option was greyed-out, because not all of the necessary information had been specified within the Booking at that point.
These methods, and other aspects of Naked Objects, can be controlled
by adding about
methods that correspond to the method
we need to control. The about
method must return an
About
object that contains a name, description and
permissions. These permissions determine whether the user is able to
access the controlled entity and whether it is currently available to
be used.
In our example the Confirm option is greyed out
because the aboutActionConfirm
method, which corresponds
to the actionConfirm
method, returned an About object
that vetoed the action with the object in it current state. The viewer
checks this About object before offering the options on the popup menu
and determines how to display the action. After all the information has
been entered within the Booking, a subsequent look at the menu will show
that this option is now enabled.
It is worth noting that if no about method is declared then an action will always be allowed.
Copyright (c) 2002 - 2004 nakedobjects.org
You may print this document for your own personal
use, or you may copy it in electronic form for access within your
organisation, provided that this notice is preserved.