vendredi 27 avril 2018

Does there exist a Pass/Fail Design Pattern?

Not quite sure what the formal term for such a pattern/problem, but here is what I'm facing:

I have an operation that is somewhat large. It can either pass or fail. Each pass or fail carries with it either the result of the successful operation, or information about why the operation failed. I am struggling to architect this function 'correctly'.

class Pass{
    int someGoodStuff;
    double someOtherGoodStuff;
}

class Fail{
    String failureMsg;
    float howMuchOperationWasOffBy;
}

class Operator{
    public ??? operation(){
    }
}

Approach 1: Fail states are like exceptions. Lets throw them. This allows me to include the failure information and make the return type just Pass. However, these fail states are not programming language errors, they are business logic fails. So, two things sit wrong with me about this approach: one, it confuses business logic fails with actual Java errors (which seems wrong) and two, it coopts the normal flow of execution without any really good reason to do so.

Approach 2: Functions in java like to return one object type, so have Pass and Fail both implement an interface Result, and have the return type of the function be that.

interface Result{...}
class Pass implements Result{...}
class Fail implements Result{...}
class Operator{
    public Result operation(){...}
}

However, the pass and fail states that the function returns are completely different. They have little to no overlapping variables or functions. This seems wrong and reduces me to have to instanceof all the important information out of the passes and fails.

Approach 3: Some sort of union object that can be either pass or fail (but not both)

class Result{
    public Pass pass=null;
    public Fail fail=null;
}

class Operator{
    public Result operation(){...}
}

This has the advantage that I can say things like

Result result=op.operation();
if (result.succeed()){
    doStuffWithPass(result.getPass());
}else{
    doStuffWithFail(result.getFail());
}

Which is basically what I was doing with instanceof, but nicer looking; the code now looks how you might expect it to. It is clear to follow.

However, Java has no real Union types. I have to make sure someone doesn't accidentally try to mess with the pass variables of a fail Result or vice versa. Furthermore, every method I call on the union type have to be predicated with figuring out whether it is a pass or fail (That If branch suddenly has to be everywhere)

Finally, although not a real concern yet, such a pattern I believe allocates space for both a Pass and a Fail for each Result, when I know that it can only ever be one of them (ie it should take up a space equal to Max(space(Pass),space(Fail)))

None of these approaches seems perfect. I feel like there should be a pattern to solve this kind of problem. Is there?

Aucun commentaire:

Enregistrer un commentaire