|By Michael Zuskin||
|August 27, 2012 04:15 AM EDT||
To pass errors from functions outward, throw exceptions instead of returning an error code like -1. That's implemented by filling the "Throws:" field in the function's header (signature) with the class Exception or its descendant. When the exceptions mechanism is used, calls to functions look very simple:
As you see, there is no terrible code impurities like
if uf_do_something() = -1 then return -1
li_rc = uf_do_something()
if li_rc = -1 then return -1
The tradition of returning a success/failure code come from ancient times when exception throwing mechanism didn't exist yet in PowerBuilder, but there's no need to use horses in the automobile era. We still check code returned by built-in PB functions, as well as in legacy application code, but be a modern person and write new code.
The Art of Exceptions Propagation
The rule of using exceptions in multi-level, multi-branch call hierarchies is simple: if you have a chain of nested function calls (uf_1 calls uf_2; uf_2 calls uf_3; uf_3 calls uf_4; etc.) then the try...catch block usually appears only in the outermost function (uf_1) - the calling hierarchies' root, decorating the calling to uf_2; functions uf_2, uf_3 and uf_4 usually don't have their own try...catch blocks - they only transfer exceptions outward. Even more - the root function also can exist without the try...catch block, passing the exception out. A good example of this is a controller NVO in which a root public function, which will be called from another script(s) of your application, calls subsidiary private functions of the NVO. In this situation, the exceptions, having come to the root function from the dark ocean depths of the calls hierarchy, is thrown out by the root function of the NVO instead of returning -1. That will add elegancy to the script that consumes your service, for example, it can be a button's Clicked event - that is really "the first station." So, calling the NVO's public function from it must be surrounded with try...catch.
Of course, the not-root functions are not prohibited from having their own try...catch blocks too, but it's a pretty rare practice that happens if they need to process an exception in their special way and then re-throw it.
(To Be) or (Not to Be)?
Calling a function that throws an exception requires special processing in the calling script. Fortunately, the rule is simple: if script A calls script B and script B throws an exception, then script A has two, and only two, choices:
- To process (i.e., to catch) the exception. For that, script A must surround the call to script B with a try...catch block.
- Not to process the exception (i.e., to pass it outwards by filling the field "Throws:" in the header of script A). In that case an outer script, calling script A, will bother deciding what to do with the exception.
You don't have to learn this rule: the compiler forces you to perform at least one of the described actions, so you have no chance to unintentionally "interrupt current" in nested, multi-level call chains by forgetting to add throwing an exception to the function's signature.
What Is Wrong with the Built-In PB Exceptions?
It's not a big deal to throw an exception in PowerBuilder 8 or later but, IMHO, three very important conditions must be taken into account:
- The error message, describing the problem, should display the class and the script where the problem occurred.
- That info must not be typed by the developer each time manually - it should be populated automatically.
- The code, throwing the exception, must be compact - in fact, it's a piece of technical code, embedded into business code, so it shouldn't be longer than one line. Imagine if your script throws a number of exceptions, and each time an exception object has to be created, populated and thrown... You'll hardly see the business logic behind all that technical garbage.
The following solution is absolutely not acceptable:
if [condition of Problem 1] then
l_ex = create Exception
l_ex.SetMessage("[description of Problem 1] in function uf_XXX of class n_YYY")
if [condition of Problem 2] then
l_ex = create Exception
l_ex.SetMessage("[description of Problem 2] in function uf_XXX of class n_YYY")
- Where Are RIA Technologies Headed in 2008?
- PowerBuilder History - How Did It Evolve?
- Creation and Consumption of Web Services with PowerBuilder
- Cloud People: A Who's Who of Cloud Computing
- DDDW Tips and Tricks
- Working with SOA & Web Services in PowerBuilder
- Cloud Expo 2011 East To Attract 10,000 Delegates and 200 Exhibitors
- Dynamically Creating DataWindow Objects
- OLE - Extending the Capabilities of PowerBuilder
- DataWindow.NET How To: Data Entry Form