"Stop the pigeon! Stop that pigeon now!"
Sheriff of Nottingham, Sherwood Forest, The Adventures of Robin Hood


"That don't impress me much!"
Shania Twain, Come on over


"Get the cheese to sickbay"
B'Elanna Torres, Starship Voyager


"What did y'all order a dead guy for?"
Jayne Cobb, Firefly


"Excuse me, but I am in the middle of fifteen things, all of them annoying"
Susan Ivanova, Babylon 5


"There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened"
Douglas Adams


place
Can/Check Operations in Problem Domain Classes
Home > SoftwareDesign > Java
There is a simple coding convention that can be adopted by PD developers that makes enabling/disabling controls in the UI easier.

Introduction

There are normally various conditions that must be checked before a public method of a problem domain (PD) class can be expected to perform correctly.

These include basic validation of values supplied as parameters to the method such as:

  • parameters that require a value are not set to null
  • string parameters are not empty when they should have a significant value
  • numbers and dates are within the ranges allowed
  • the value of one parameter does not conflict with another. For example, where two date paramters represnt an interval of time, the start date of the interval is not after the end date of the interval.
More sophisticated pre-condition checks include:
  • checking an object to ensure it is in the right state to run a particular operation. For example, checking a loan application has been approved before the implement() operation is run to transfer funds to the borrower's account.
  • checking the current user has the authority to run the operation with the specified parameter or object values. For example, the loan officer is allowed to run the approve() operation on an UnsecuredLoan object for a loan over $10,000.
Non-compliance with of pre-conditions indicates either a programming error or a problem with user specified data. In modern object-orient programming languages like Java, .Net languages and Objective-C, failures of a pre-condition are usually dealt woth by throwing an exception. Typically calling software catches the exception and either gracefully halts the program with a suitable error message, or falls back to a known safe state and, after presenting the user with a suitable error message, allows the user to continue.

Enabling UI components

A good user interface (UI) dynamically enables and disables buttons and menu options so only the operations permitted are available to the user. For example, the Approve button is only enabled on the loan view if the user has authority to approve the size of loan currently being viewed.

Note: User interface can make buttons visible/invisible, or disabled/enabled dynamically as object state changes, etc. Disabled/Enabled is generally preferable to visible/invisible; invisible can lead to a user searching in vain for a button. Disabled emphasis allows the user to see the button but indicates that it is not allowed to be pressed by the user for some business reason. This is widely acknowledged as better UI practice but you do lose something. If all buttons are enabled, the user can be told exactly why an operation cannot be performed when they try to invoke it. A disabled button does not provide that; the user can be left wondering what they must do to enable the button. One solution is a tooltip on a disabled button that explains why the button is disabled (i.e. describes the check that failed).

For a user interface to be able to enable and disable operations, PD services are required that return boolean values indicating if the prevailing conditions permit an operation. The user interface does not want to call multiple validation methods catching various exceptions just to set the enabled property of a user interface component to false; the exception catching code has to be repeated wherever the operation can be invoked. This is too much work and change is not localised.

In contrast, a simple user interface may not dynamically enable/disable buttons and menus, allowing users to invoke operations without restriction. In this case, users do want more than simply whether an operation was successful or not. They want a clear and specific error message that explains why the operation failed. This user interface, therefore, the user interface wants the PD to throw specific exceptions; returning a boolean value is not good enough. Obviously this contradicts the requirement for providing a simple boolean indication to allow enabling and disabling controls in a more sophisticated user interface.

Of course, in a non-trivial application some parts of the user interface may be more sophisticated than others, and the PD cannot predict where a boolean indication of failure is required and where detailed exceptions are required.

Implementation convention

Whether you are coding in Java, C#, or Objective-C, a simple coding convention can be adopted by PD developers that supports both of the above requirements for a little extra work.

The PD class provides a number of checkOperation() methods that check the preconditions associated with a method. These methods return void and throw a specific exception if one of the pre-conditions is not satisfied. Then for each check method, the PD class also provides a canOperation() method within a try-catch statement. Each of these can methods invokes the equivalent check method. The can methods returns false if an exception is thrown by the check method. Otherwise they return true.

Adopting this convention can obviously lead to the precondition checks being executed several times. Where this degrades performance below an acceptable level, the checks can often be optimised by storing intermediate results in transient attributes of the PD class. For example, a check to determine if the logged in user is the owner of a particular object can be done once and result saved; for a particular session the logged in user and object owner typically do not change. If the owner can change then the method that sets the new owner can also reset the check result.

In practice, this convention is useful in:

  • reducing UI developers work,
  • making the UI feel more consistent
  • encouraging the consistent checking of pre-conditions

In more complex objects, check methods often delegate to smaller check methods. The class developer must decide how granular to make the can methods in such a scenario. Since the can methods are simple to code, if logistics allow, the PD developer can let the UI developers ask for can methods to be added when they discover that they are needed.

Test/Do

Nicola, Mayfield and Abney suggest implementing public problem domain class operations in three parts:
  1. a testXXX method that checks that the operation may be performed. This is similar to the check methods described above. It throws exceptions if the one of the checks fails.
  2. a private doXXX that perfroms the actions required by the operation
  3. the public XXX method that simply calls the testX method followed by the doX method.
This technique complements our check/can pattern very nicely. Having the private doX means objects of these classes can bypass the pre-condition checking when they need, or when they know the pre-conditions have been met.

Additional Alternative

In addition to the check and can methods, the PD class developer can choose to add checkAllOperation() methods. The check methods stop at the first sign of trouble; as soon as one of the precondition checks fails. The checkAll methods continue checking all pre-conditions, adding each exception caught to a suitable collection. After all conditions have been checked an enumeration over the collection is returned to the caller. An empty enumeration means all checks passed.

The checkAll methods require the user interface to display a list of problems to user in a manner somewhat analogous to the output from a compiler. Obviously there is higher overhead in checking all conditions and these methods should only be added where appropriate. One example of a suitable place might be just before a complex loan, license or insurance form is submitted for approval. This approach avoids the ever-annoying 'fix the problem, try again, get told about another problem' cycle.

Summary

Using check and can methods provides the UI with the flexible validation services it desires, enhances UI and precondition checking consistency and saves UI developers time. The cost is a little extra work in the PD classes and the need to optimise the effects of repeating condition checks in places.

Acknowledgments

Most of the above derives from discussions on the PowerLender project at United Overseas Bank.