- You are working with methods that either succeed and return some object, or fail. Many parser methods and rule engine methods are like this.
- One way to handle this is to define the result of nil as a failure, and any other object as success. The problem with this is that traditionally nil means "no data" which is not always the same as "method failure". Sometimes no data is a successful result.
- Defining false as a failure and any other object as success doesn't feel right, either.
- Another way to handle this is to define a Result object. The result object is either a success result object which contains the desired data, or a failure result object that may contain a reason for the failure. This works, but the extra object is awkward.
- You want a simpler way.
Therefore, define
ObjectAsSuccess, and create a new Failure class. Any method result that is not of class Failure is by definition a successful result.
- Thus, you are adding the object dichotomy success/failure to the existing object dichotomies of true/false and nil/notNil.
Example methods
Object>ifSuccess: aBlock
Object>ifFailure: aBlock
Object>ifSuccess: successBlock ifFailure: failureBlock
Object>isSuccess
Object>isFailure
Failure>ifSuccess: aBlock
Failure>ifFailure: aBlock
Failure>ifSuccess: successBlock ifFailure: failureBlock
Failure>isSuccess
Failure>isFailure
Example code
|result |
self rules do: [ :each |
result := self fireRule: each with: self.
result ifSuccess: [^result].
].
^ Failure onError: 'No matching rule'.
How does this compare to throwing an exception on failure? One difference is whether you're most likely to just stop processing and bail out (in which case exceptions look cleaner), or to try to continue or repair things on the spot (in which case lots of catch/onExceptionDo: blocks become tedious). Also, I'd be inclined to add a data field to the Failure class so that you can pass back some clue as to why the failure occurred. --SteveFreeman
CategorySuccess