Exforsys

Online Training

Best Practices for Checking Prerequisites

This is a discussion on Best Practices for Checking Prerequisites within the Software Patterns forums, part of the Testing category; Greetings, I've been reviewing the code for software that I've been working on for a couple years. I'...


Go Back   Exforsys > Testing > Software Patterns

Register FAQ Members List Calendar Search Today's Posts Mark Forums Read
  #1 (permalink)  
Old 08-19-2004, 02:19 PM
Mario T. Lanza
Guest
 
Posts: n/a
Best Practices for Checking Prerequisites

Greetings,

I've been reviewing the code for software that I've been working on
for a couple years. I'm looking at some of the situations that I've
come across and deciding what I can learn from them and how I can best
handle similar situations in the future.

Since I am sure that my situation is a familiar one to many of you, I
am wondering what practices many of you have formed over the years.
Perhaps there is even a published best pattern/practice for my
scenerio.

Here it is:

I have certain methods that users direct my software to call in order
to perform certain business-specific tasks. To avoid speaking in
generalities, I'll provide a specific example.

My software allows tellers (like bank tellers) to transact with
customers (as one would do at a bank or a supermarket). At the
beginning of the day when a teller reports to work he must assign
himself a cash drawer over which cash contents he will be held
entirely accountable. My software has methods that correspond to the
actions a teller may take over the course of a day's business. Two
that come immediately to mind are 1) the assigning of a drawer and 2)
the unassigning of a drawer. Let's focus on the latter for now:
Unassigning a drawer.

My .NET C# code has a method: UnassignDrawer(DrawerData.DrawerRow
drawer)

Imagine also that my application has a button entitled "Unassign
Drawer" (it could have just as well been a menu option in addition to
or instead of). When the user clicks this button, the application
calls the UnassignDrawer method.

Now, understand that the drawer unassignment process has a number of
prerequisites. Before a drawer may be unassigned the teller must
have:

1. Deposited all his checks (using another method that flags his
checks as deposited)
2. Audited (counted out) the cash in his drawer (by denomination so
that database may track the exact count of each cash/coin
denomination)
3. Closed out any two-sided transactions (as a banker would first do a
withdrawal transaction and then a second deposit transaction to
transfer funds between accounts as part of one larger overall
transaction)
....
....
.... (etc.)

The number of procedural steps in unimportant. What is important is
that there may be any number of business-related prerequisites that
the teller must meet before unassigning the drawer.

As part of my method UnassignDrawer method I have another method
entitled AllowUnassignDrawer(DrawerData.DrawerRow drawer). This
method is called early within the UnassignDrawer method. If the
prerequisites have not been met, the teller is alerted (with a list)
as to the actions he must take in order for the unassignment to be
allowed.

An interesting question I've asked myself is this: Do I disable the
"Unassign Drawer" button until the prerequisites have been met? If I
do, then I will have to call the AllowUnassignDrawer method as the
state of the application (the requisite conditions) changes in order
to enable/disable the button. Whenever the prerequisites are checked
there is overhead associated with querying the database. Even if this
overhead takes only 3 seconds to check the multiple conditions, it
seems wise to query the database as infrequently as possible.
Futhermore, since I have to call the AllowUnassignDrawer method in
order to disable/enable the button and then call it again from within
the UnassignDrawer method itself (as a safegaurd against allowing the
drawer to be unassigned if the button is ever inadvertently enabled
when it should not have been), it seems that it may be necessary to
call the AllowUnassignDrawer method twice.

Presently, I leave the "Unassign Drawer" button enabled (so long as
the teller has a drawer assigned). Then when the teller clicks the
button, I report any failed prerequisites in a panel at the side of
the screen. The teller then performs the tasks to meet the
prerequisites before clicking the "Unassign Drawer" button again.

My thinking was:

If I go ahead and disable the button while the prerequisites are not
met then I must be responsible for calling the prerequiste checking
function (AllowUnassignDrawer) more frequently in order to maintain
the enable/disabled status (even if the user does not wish to unassign
the drawer). Conversely, when I call the prerequisite checking
function only when the user wishes to take the action, I alleviate the
overhead of maintaining the enabled/disabled status of the button.

Furthermore, by perpetually maintaining the button status I may
confuse the user who will wonder why he is unable to unassign his
drawer (because he cannot click the disabled "Unassign Drawer" button
in order to review the prerequisite checklist). If I choose to provide
some other option for reviewing the checklist, where would it best be
located? In my opinion, it would best be located as close to the
"Unassign Drawer" button as possible so that the user can easily
determine why the button is disabled in the first place.

The other issue with my AllowUnassignDrawer method is that it returns
a boolean value indicating whether or not the drawer may be
unassigned. Since the scenerio at hand deals with potentially
numerous prerequisites the one fact (the true/false response provided
by the AllowUnassignDrawer method) is insufficient. I need some other
output to communicate the various reasons (the failed prerequistes) to
the user. In my case, I invoke an event to display a filled out
checklist.

Since the AllowUnassignDrawer method doesn't return the checklist (it
invokes the event), the event listener (which is the application's
primary form itself) must then go ahead and build the checklist
thereby calling the database again for each of the prerequisite
conditions. Obviously, I could have built more elaborate event
argument/handler classes to communicate the checklist conditions
directly to the listener.

I'm not concerned at all with this specific example. I provided it
only as framework on which to more clearly communicate the issue at
hand.

In my case I have a method that facilitates a business task
(UnassignDrawer), a method that tests for permission to perform the
particular business task (AllowUnassignDrawer), and an event that is
subscribed to by the primary form in order to display the numerous
prerequisite conditions to the user (the user interface). This is one
design that evolved as the application evolved. It wasn't one that I
fully thought out and planned in advance. I make this post to
faciliate a discussion on the best practices/patterns surrounding the
handling of prerequisite conditions associated with specific business
tasks.

Your ideas and practical examples are appreciated.

Mario T. Lanza
Clarity Information Architecture, Inc.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #2 (permalink)  
Old 08-28-2004, 07:00 PM
cstb
Guest
 
Posts: n/a
Re: Best Practices for Checking Prerequisites



Mario T. Lanza wrote:

.... snip ...

> In my case I have a method that facilitates a business task
> (UnassignDrawer), a method that tests for permission to perform the
> particular business task (AllowUnassignDrawer), and an event that is
> subscribed to by the primary form in order to display the numerous
> prerequisite conditions to the user (the user interface). This is one
> design that evolved as the application evolved. It wasn't one that I
> fully thought out and planned in advance. I make this post to
> faciliate a discussion on the best practices/patterns surrounding the
> handling of prerequisite conditions associated with specific business
> tasks.
>
> Your ideas and practical examples are appreciated.


Patterns: Observer (esp. MVC), Adapters, Checks

Practice: Separation of concerns

a)

Build a complete and consistant model of the system.
By "complete" is meant "has interfaces which allow
all needed manipulations and/or which produce any and
all data which is of interest to the outside world."
By "consistant" is meant "every manipulation available
through such an interface either succeeds (prereqs are
met, invariants are maintained, change is applied, and
any data provided to outside world from now on reflects
the change), or fails (prereqs not met, changes not
applied, any data provided to outside world remains
as it was before the attempted manipulation)".

b)

When finished, attach Views and Controllers to it.

If you have first accomplished (a), then (b) is nearly
trivial. As an example, the notion of a button which
is enabled only when all prereqs are met, becomes:

Attach a controller which sends "releaseDrawer"
whenever it is both enabled and pressed.

Attach a view which observes the "drawerIsReleaseable"
boolean, and which enables/disables the aforementioned
controller (button) accordingly.

Note that "drawerIsReleaseable" is a boolean maintained
by the model. If you keep this in mind, you needn't
"ask permission", you can instead "observe availability".
This boolean *must* be changed (recalculated) whenever
anything it depends on is changed -- it *can* be recalculated
more often than this (such as every time a caller needs the
current value) -- but it *must* be recalculated whenever it
could actually change. You are free (as designer) to pick
any frequency of update between these two extremes. Such
choices affect "performance" and "usability", but not
correctness. MVC allows one to optimize update frequency.

Another question you can ask yourself is whether the
"releaseDrawer" function is distinct, or just one of the
subtasks the user must complete. Consider keeping a list
of unfinished subtasks (in the model), each of which is
enabled/disabled according to the current state of the model.
Consider keeping the list sorted in "a proposed order", such
that the "releaseDrawer" subtask is always last in the list.
Attach controllers/views as before, observing this list.


Regards,

-cstb

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #3 (permalink)  
Old 09-01-2004, 06:51 PM
Mario T. Lanza
Guest
 
Posts: n/a
Re: Best Practices for Checking Prerequisites

Thanks for your detailed response.

Mario
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



All times are GMT -4. The time now is 04:41 PM.


Powered by vBulletin® Version 3.7.3
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.1.0
Copyright 2004 - 2007 Exforsys Inc. All rights reserved.