In a simple scenario, I have a class that is responsible for retrieving data from the "customer" table. Previously I mixed'n'matched and this class was also used for storing customer data in properties.
Now, I've changed that, and use a "value" object for passing "just" data around. So , in the most simple form, this class is a property for each field of the customer table. The Customer class retrieves the data, creates a new "customer value" class and returns that class as a result of the method.
/* pseudo-code*/
CLASS value.customer:
DEF PUBLIC PROPERTY Name AS CHAR NO-UNDO GET . SET .
END CLASS.
/* pseudo-code*/
CLASS Customer:
..
METHOD PUBLIC value.customer Read(Customer-num AS INT):
DEF VAR myCustomer AS value.customer NO-UNDO.
..
MyCustomer = NEW value.customer.
..
FIND Customer etc etc.
..
MyCustomer.Name = Customer.Name
..
RETURN MyCustomer.
END METHOD.
Now, (finally) here's my question: What if the customer record is not found.
1) RETURN ERROR
2) RETURN ?. (unknown)
3) RETURN BlankObject
I got bit by this today (my fault, didn't check for safe return value) (and in a "hotfix" replaced one option with another) but was wondering what other people do.
jmls wrote:
In a simple scenario, I have a class that is responsible for retrieving data from the "customer" table. Previously I mixed'n'matched and this class was also used for storing customer data in properties.
Now, I've changed that, and use a "value" object for passing "just" data around. So , in the most simple form, this class is a property for each field of the customer table. The Customer class retrieves the data, creates a new "customer value" class and returns that class as a result of the method.
Now, (finally) here's my question: What if the customer record is not found.
1) RETURN ERROR
2) RETURN ?. (unknown)
3) RETURN BlankObject
I got bit by this today (my fault, didn't check for safe return value) (and in a "hotfix" replaced one option with another) but was wondering what other people do.
You could try the Null Object pattern: http://en.wikipedia.org/wiki/Null_Object_pattern .
There's also the option of doing what the System.EventArgs object does, and have an Empty property, which is basically a singleton, empty object. This is a variant of #3
define static public property Empty as EventArgs
get():
if not valid-object(EventArgs:Empty) then
EventArgs:Empty = new EventArgs().
return EventArgs:Empty.
end.
private set.
But to me the the question is really one of the app design: how would you deal with this in a non-object world? Is there error handling (IF NOT AVAILABLE .. )? That should provide guidance on how you should deal with this in the OO world.
My personal inclination is to return null, but that does have issues.
-- peter
Good for you for separating DL from BL with a message!
I would think that if you return your MDP (message data packet) that it should contain ?, but it also seems like the right place to throw an exception.
If a record not found is a exceptionnal case and should never happens, then I would use UNDO, THROW + ROUTINE-LEVEL ON ERROR UNDO, THROW.
On the other hand, if this method is used frenquently to know if a record exist, then I think I would RETURN ?.
Or you could use another approach like this:
DEF VAR myCustomer AS value.customer NO-UNDO.
MyCustomer = NEW value.customer().
MyCustomer:Customer-num = 1.
if Customer.Fetch(MyCustomer) then
do: /* record found */
end.
Maybe way off topic, but you can have a look at LLBLGen Pro documentation for some interesting ideas for interfaces to your O/R mapper.
http://www.llblgen.com/documentation/2.6/hh_start.htm
See sections "Generated code - Using the entity classes, Adapter" and "Generated code - Using the entity collection classes, Adapter"
It was returning NULL (#2) that bit me . Yep, I know, I should have
seen it coming. However, it screwed up my webspeed output . I don't
know what exactly happened (either the procedure raised an error when
I tried referencing a property of the non-existent object, or it
returned ?. Either way I was screwed, so replaced quickly with a blank
object (#3).
Now that the "patch" is in, just looking at what I could have done /
should have done, and wondering how others deal with this. At the end
of the day,
#1)
CATCH
END CATCH
#2)
IF VALID-OBJECT(foo)
#3)
IF foo:Name EQ ""
all do the same thing, I suppose.
Thanks
Julian
The short
You have a good point here Guillaume ... what you do really should depend on what you expect. If it is routine and normal not to find a customer, then you should have a signal, like ?, that says "I didn't find one this time", but, if it is a context where you really expect to find the customer, e.g., the number comes from an order, then throw an exception.
I am taking that as a compliment. I may be wrong ....
So you advocate either #1 or #2
Interesting,
Good idea. Thanks.
It was a compliment.
Holy cow.
* jmls prints the above comment on the best cartridge paper, commandeers
a frame and puts it on the wall.
can't take it back now !
On 3 May 2010 20:26, Thomas Mercer-Hursh
You and the 10.2B01 migration (see his PEG thread) over the weekend have saved his day.
Lucky Julian!
I feel so lucky and blessed today !
Today is a good day. (tm)