How to handle a missing object

Posted by jmls on 03-May-2010 13:43

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.

All Replies

Posted by Peter Judge on 03-May-2010 13:54

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

Posted by Thomas Mercer-Hursh on 03-May-2010 13:57

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.

Posted by guilmori on 03-May-2010 13:58

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"

Posted by jmls on 03-May-2010 14:06

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

Posted by Thomas Mercer-Hursh on 03-May-2010 14:17

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.

Posted by jmls on 03-May-2010 14:20

I am taking that as a compliment. I may be wrong  ....

So you advocate either #1 or #2

Interesting,

Posted by jmls on 03-May-2010 14:20

Good idea. Thanks.

Posted by Thomas Mercer-Hursh on 03-May-2010 14:26

It was a compliment.

Posted by jmls on 03-May-2010 14:31

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

Posted by Admin on 03-May-2010 14:36

You and the 10.2B01 migration (see his PEG thread) over the weekend have saved his day.

Lucky Julian!

Posted by jmls on 03-May-2010 14:56

I feel so lucky and blessed today !

Today is a good day. (tm)

This thread is closed