Protego – smart way to manage nil in business calculation

Imagine, you have some object with amount and price variables.
And you want to calculate total, put this totals to collection and calculate grand total.

Naive implementation will be something like
total := amount * price for the first task
and something like
grandTotal := totals sum for the latter one.

But what if amount or price are nil’s?
You can add nil checks, of course: total := (amount ifNil:[0]) * (price ifNil: [0]).
And then what if totals will be empty? More checks.
Is it really wise to have total to be zero if amount is nil? More ifTrue:ifFalse: code.
And then you want to add amount := total / price calculation, and you realize that both total and price may be nil, and price may be zero.
More and more and more checks.

Protego (http://www.smalltalkhub.com/#!/~assargadon/Protego) adds “protected” versions of common operators and methods.

So, you just put
total := amount *@ price
and
grandTotal := totals sum_protected
into your code – and everything just works.

total will be nil if any of the operands are nil. grandTotal will be calculated normally, even for empty totals collection (will return nil for empty collection).
Collection can hold nil’s and it will work anyway. If all elements of the collection is nil, result will be nil.

Protected version of addition and subtraction will treat nil’s as zeros until both operands are nil – then it will return nil.
There are protected versions of comparisions, too.

It’s quite simple idea, which is still very useful, and it makes code much more readable, clean and self-commenting.

That’s why I have extracted this part of SmallPOS framework, and have published it as separate package to use it in other, non-SmallPOS applications.

Юрий Мироненко <assargaddon@gmail.com>

 

Ben coman proposed a pattern

Thanks for sharing. A similar thing might be useful for dealing with
“quality” of Process Variable measurement in industrial situations –
where instruments fail from time to time.

I’m not sure of all the pros and cons, but btw an alternative to
hanging special behaviour on a method, would be to hang special
behaviour on a MissingValue class that you could #initialize any
instance variable to.

Object subclass: #MissingValue
instanceVariableNames: ”
classVariableNames: ”
package: ‘AAAA’

MissingValue >> * dummyValue
self inform: ‘debug trace – got here’.
^self

MissingValue >> adaptToNumber: rcvr andSend: selector
^self perform: selector with: rcvr

Collection >> sumProtected
^ self inject: 0 into: [ :sum :each | sum + each value]

Usage…

c := {1.1 * MissingValue new. 1.2 * 10. MissingValue new * 1.3. 10 * 1.4}.
“==> an Array(aMissingValue 12.0 aMissingValue 14.0)”

c sumExcludeMissing
“==> 26.0”

cheers -ben

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: