We have seen several examples of controlling access to classes,
objects, fields and methods using an about... method
that corresponds to a class, object, accessor or action method. These
methods all return an
org.nakedobjects.object.control.About object. The
About object provides four pieces of information about the
class, object, field or method it is supporting, for potential use by the
viewing mechanism or any other service. These are:
- Whether it should be accessible to the current user.
- Whether it can be used while the object is in its current
state.
- The name it should be known by (if it needs to differ from the
name that is automatically generated by the framework from the class
or method name).
- A description of the class, object or method it is
controlling.
All About objects are derived from the
About interface. This interface declares four methods
that represent the information mentioned above:
-
canAccess
-
canUse
-
getName
-
getDescription
The last two return String objects, whilst the first
two each return a
org.nakedobjects.object.control.Permission object. A
Permission object is used instead of a simple boolean
because in addition to specifying whether the access/use has been
allowed or disallowed, it can also provide a reason, in the form
of a textual message. This is further simplified by the fact that the
Permission class is sub-classed as
org.nakedobjects.object.control.Allow and
org.nakedobjects.object.control.Veto , which simply hold
a reason String and indicate the state by their
type.
Using the About interface you can define your own
About class and exercise complete control over your
objects. However, the ready-made About classes
provided by the framework will be sufficient for most purposes.
Ready-made About objects
The simplest way to apply control is to use ready-made
About objects. All of the basic About
classes (such as
org.nakedobjects.object.control.ClassAbout ,
org.nakedobjects.object.control.FieldAbout and
org.nakedobjects.object.control.ActionAbout ) have publicly-available
constants (such as UNINSTANTIABLE ,
READ_ONLY and ENABLE , which
determine the usability of classes, fields and methods
respectively).
To provide some flexibility these classes also have static methods
that will provide an About object based on a flag that
you supply. For example ActionAbout has the method
enable that takes a boolean and will
return the ENABLE object if the flag is
true and the DISABLE object if
false . There is also a complementary method called
disable . This allows us to disable an
action... method on a specified condition, as shown
below:
public About aboutActionSell() {
return ActionAbout.disable(getCustomer() == null);
}
Building About objects
When you need an About to do more than just disable
something (for example, you want to change a field or method name),
then the ready-made About objects we have discussed so far
are not suitable as they have no provision for conveying any
additional information. Things are even more complicated when there
are a number of influences that each partly determine whether something
should be allowed or not. For example, consider the
Location class. It has a method
actionNewBooking(Location) that allows two locations
to be used to create a new Booking using the two
locations as pick-up and drop-off points. This method should only be
allowed if the two location objects are different, and that they are also
in the same city. If we implement the about... method
using the static About objects (as shown below) then the
users will know when they can't drop an object, but will not know
why:
public About aboutActionNewBooking(Location location) {
boolean differentLocations = !equals(location);
boolean sameCity = (getCity() != null) && getCity().equals(location.getCity());
return ActionAbout.enable(differentLocations && sameCity);
}
We need to be able to include the reason why something is
disallowed. This is catered for in the Permission objects
that we mentioned earlier. These are used within the
org.nakedobjects.object.control.ProgrammableAbout class,
which allows us to build up an About object by explicitly
setting its state or conditionally modifying it as conditions are
checked. For example, here is an alternative version of the previous
method using this class:
public About aboutActionNewBooking(Location location) {
boolean differentLocations = !equals(location);
boolean sameCity = (getCity() != null) && getCity().equals(location.getCity());
ProgrammableAbout about = new ProgrammableAbout();
about.makeAvailableOnCondition(differentLocations,
"Two different locations are required");
about.makeAvailableOnCondition(sameCity,
"Locations must be in the same city");
return about;
}
Using the same two flags we've added these conditions to the
ProgrammableAbout object using the
makeAvailableOnCondition method. This does nothing if
the flag is true , but if it is false then this
method ensures that a subsequent call to canUse will
return a Veto object that includes the specified text as
the reason, or part of the reason, that this option is not available.
This information will be displayed to the user when the drop is invalid
and describes whether the locations are the same or are in
different cities.
The following version further extends the method so that the
About now contains a description of the method. This is
in line with our philosophy that the documentation should be part of
the code and not written and maintained separately. It could be used
(although it is not at the moment) within the framework to describe the
option to the user at the point where he might want to use it.
Alternatively, it could be used by a program that automatically
generates the complete documentation for an application. This version
also shows the changeNameIfAvailable method that sets
up the name of the About so that it reflects the action
to be performed. Using this version of the method, the name will only
be changed if the action is allowed:
public About aboutActionNewBooking(Location location) {
boolean differentLocations = !equals(location);
boolean sameCity = (getCity() != null) && getCity().equals(location.getCity());
ProgrammableAbout about = new ProgrammableAbout();
about.setDescription("Giving one location to another location creates " +
"a new booking going from the given location " +
"to the receiving location.");
about.makeAvailableOnCondition(differentLocations,
"Two different locations are required");
about.makeAvailableOnCondition(sameCity,
"Locations must be in the same city");
about.changeNameIfAvailable("New booking from " + location.title() +
" to " + title());
return about;
}
Areas of control
The following summarizes what aspects of a naked object can be
controlled and how it should be done.
-
A class
Add a static aboutClass
method to
class.
Use ClassAbout to make a class
uninstantiable.
-
An object
Add an about method to class.
Use ObjectAbout or the
ProgrammableAbout to make an instance
read-only.
-
A field
For value objects assign an About using the value
object's setAbout method.
For associations, add aboutVariable
method to the class matching the
getVariable
method you wish to
control.
Use FieldAbout to make an field read-only or the
ProgrammableAbout to make an field read-only or change
the name of the field.
-
An action method
Add an aboutActionMethod
method to the
class matching the actionMethod
method.
Use ActionAbout to disable the method or the
ProgrammableAbout to disable the method or change the
name of the option.
|