Code Comments
Programming Forum and web based access to our favorite programming groups.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. NOTE: Originally posted on comp.software.patterns. Cross-posted because the example is C# specific and because of lack of feedback on original newsgroup.
Post Follow-up to this messagePowered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.