tags:

views:

228

answers:

10

**EDIT: There are several options below that would work. Please vote/comment according to your views on the matter.

I'm working on cleaning up and adding functionality to a c# method with the following basic structure:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1();
            if (!step1succeeded)
                return Status.Error;

            bool step2suceeded = performStep2();
            if (!step2suceeded)
                return Status.Warning;

            //.. More steps, some of which could change returnStatus..//

            bool step3succeeded = performStep3();
            if (!step3succeeded)
                return Status.Error;
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;
        }
        finally
        {
            //some necessary cleanup
        }

        return returnStatus;
    }

There are many steps, and in most cases step x must succeed in order to proceed to step x+1. Now, I need to add some functionality that will always run at the end of the method, but which depends on the return value. I'm looking for recommendations on how to cleanly refactor this for the desired effect. The obvious choice would be to put the functionality that depends on the return value in the calling code, but I'm not able to modify the callers.

One option:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1();
            if (!step1succeeded)
            {
                returnStatus =  Status.Error;
                throw new Exception("Error");
            }

            bool step2succeeded = performStep2();
            if (!step2succeeded)
            {
                returnStatus =  Status.Warning;
                throw new Exception("Warning");
            }

            //.. the rest of the steps ..//
        }
        catch (Exception ex)
        {
            log(ex);
        }
        finally
        {
            //some necessary cleanup
        }
        FinalProcessing(returnStatus);
        return returnStatus;
    }

This seems a little ugly to me. Instead, I could throw right from the performStepX() methods. However, this leaves the problem of setting the returnStatus variable appropriately in the catch block of processStuff(). As you may have noticed, the value returned on failure of a processing step depends on which step failed.

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1(); //throws on failure
            bool step2succeeded = performStep2(); //throws on failure

            //.. the rest of the steps ..//
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;  //This is wrong if step 2 fails!
        }
        finally
        {
            //some necessary cleanup
        }
        FinalProcessing(returnStatus);
        return returnStatus;
    }

I would appreciate any suggestions that you might have.

A: 

Can you make performStep1 and performStep2 throw different exceptions?

Daniel A. White
That did occur to me. What do other people think about that approach?
Odrade
I would throw exceptions that make sense.
Daniel A. White
Do you mean throw exceptions with custom types, named to clearly reflect their purposes?
Odrade
I think it's a good idea to throw and catch 2 distinct types of exceptions. Also, it's a bad idea to catch just an Exception type as you did in your sample code. If there is some unexpected exception in your performStep1 code, you want to tell the user/programmer that something went wrong, and it's not just a failed step.
Meta-Knight
Throwing exceptions won't allow execution to continue for warnings. At least not with several large try ... catch blocks.
jheddings
+3  A: 

You could refactor the steps into an interface, so that each step, instead of being a method, exposes a Run() method, and a Status property - and you can run them in a loop, until you hit an exception. That way, you can keep the complete information on what step ran, and what status each step had.

Mathias
As a slight modification, the interface Run() method could return true or false, allowing the status field to be queried only for failed steps. The exception could be thrown in the loop if the status is Error. This allows the loop to continue for Warnings.
jheddings
This really depends on whether or not the steps actually belong in their own classes or not. Creating classes for each step, creating an interface and then keeping track of all of the objects could become overly burdensome if this is not the case.
jasonh
@jasonh True, but the management of the steps and order of execution could be handled using factories. If this solution is implemented correctly, even adding steps or changing the order of execution could be accomplished without changing code and recompiling.
Jeremy Seghi
@jasonh: first, all steps share a common functionality (execution and status), and the process is already stated as a succession of "steps" so for me the argument that there is an interface here seems strong. Then, if there are really only 3 steps and the design won't change, then this might be overkill - but otherwise, this is a fairly flexible and maintainable approach.
Mathias
+1  A: 

Get a tuple class. Then do:

var steps = new List<Tuple<Action, Status>>() {
  Tuple.Create(performStep1, Status.Error),
  Tuple.Create(performStep2, Status.Warning)
};
var status = Status.Success;
foreach (var item in steps) {
  try { item.Item1(); }
  catch (Exception ex) {
    log(ex);
    status = item.Item2;
    break;
  } 
}
// "Finally" code here

Oh yea, you can use anon types for tuples:

var steps = new [] {                        
    { step = (Action)performStep1, status = Status.Error }, 
    { step = (Action)performStep2, status = Status.Error }, 
};                                          
var status = Status.Success          
foreach (var item in steps) {               
  try { item.step(); }                     
  catch (Exception ex) {                    
    log(ex);                                
    status = item.status;                    
    break;                                  
  }                                         
}                                           
// "Finally" code here
MichaelGG
This wouldn't allow code execution to continue in the presence of a warning at performStep1.
jheddings
Also, the method doesn't follow the pattern I'm showing %100. Some steps fetch data that has to be passed to later steps.
Odrade
@jheddings as I saw it, any exception triggers the end of processing; just the status changes. @odrade, well the original just had performStep1/2/3()... :)
MichaelGG
Nice job with the abstractions... Too bad Actions's don't return bool or this could work perfectly.
jheddings
If you want the bool, just use Func<bool> instead of Action...
MichaelGG
Perfect... I hadn't seen that generic before. +1
jheddings
+1  A: 

You can perform the processing in your finally section. finally is fun in that even if you've returned in your try block the finally block will still get executed before the function actually returns. It will remember what value was being returned, though, so you can then also ditch the return at the very end of your function.

public void processStuff()
{
    Status returnStatus = Status.Success;
    try
    {
        if (!performStep1())
            return returnStatus = Status.Error;

        if (!performStep2())
            return returnStatus = Status.Warning;

        //.. the rest of the steps ..//
    }
    catch (Exception ex)
    {
        log(ex);
        return returnStatus = Status.Exception;
    }
    finally
    {
        //some necessary cleanup

        FinalProcessing(returnStatus);
    }
}
John Kugelman
+8  A: 

Don't use exceptions to control the flow of your program. Personally, I would leave the code as it is. To add the new functionality at the end you could do:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            if (!performStep1())
                returnStatus = Status.Error;
            else if (!performStep2())
                returnStatus = Status.Warning;

            //.. More steps, some of which could change returnStatus..//

            else if (!performStep3())
                returnStatus = Status.Error;
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;
        }
        finally
        {
            //some necessary cleanup
        }

        // Do your FinalProcessing(returnStatus);

        return returnStatus;
    }
bruno conde
I definitely agree with the first-half of your answer. There are some good answers here that would improve readability without violating that principle.
jheddings
As it is, it doesn't behave as required. I need to do something at the end of the method that depends on the return value. And, as I say, I can't change the calling code.
Odrade
Sorry @Odrade I forgot that you needed to change the functionality. See if my updated answer helps you.
bruno conde
+1 for pointing out that exception handling shouldn't be control flow of execution
Jeremy Seghi
This seems like a pretty clean approach. Thanks.
Odrade
The only trouble here is that you wouldn't be able to conditionally execute subsequent tests. i.e. once a 'performStepN' returns false, the remaining steps will not be executed. (referring to "in most cases step x must succeed in order to proceed to step x+1")
jheddings
-1 Unless you care about performance, if your code is clearer by using exceptions, _then do it_.
MichaelGG
To clarify, it's just an unfortunate situation that exceptions are so dreadfully slow on .NET that you need to go out of your way to avoid them, even when they would be perfectly clear and a nice choice for flow.
MichaelGG
There is definitely a readability degrade when using exceptions to control the flow of a program. Exceptions are for exceptional situations, the ones you don't expect.
bruno conde
They don't have to be. OCaml, for example, uses exceptions quite regularly, but their exception speed doesn't suck like .NET's so it's an acceptable solution in more cases.
MichaelGG
And I'm not saying you should always use exceptions for control flow, just when it's clear. And in this case, knowing that performStep12345 will throw, I think it's extremely clear...
MichaelGG
A: 

You could reverse your status setting. Set the error status before calling the exception throwing method. At the end, set it success if no exceptions.

Status status = Status.Error;
try {
  var res1 = performStep1(); 
  status = Status.Warning;
  performStep2(res1);
  status = Status.Whatever
  performStep3();
  status = Status.Success; // no exceptions thrown
} catch (Exception ex) {
  log(ex);
} finally {
 // ...
}
MichaelGG
And this makes the code clearer how?
bruno conde
"Failure by default"? This lets him throw exceptions from each step as he wanted, while making sure the failure for each step is set. No extra conditionals or more code. C#'s sorta limited when it comes to refactoring at such a small level.
MichaelGG
A: 

what about nested if?

could work and could not work

it would remove every return to leave only one

if(performStep1())
{
  if(performStep2())
  {
      //..........
  }
  else 
    returnStatus = Status.Warning;
}
else
  returnStatus = Status.Error;
Fredou
That could work, but it would lead a deeply-nested monstrosity.
Odrade
A: 

Not knowing to much of what you're logical requirements are, I'd start with creating an abstract class to act as a base object to execute a specific step and return the Status of the execution. It should have overrideable methods to implement task execution, actions on success, and actions on failure. also handle the logic Putting the logic in this class to handle success or failure of the task:

abstract class TaskBase
{
    public Status ExecuteTask()
    {
        if(ExecuteTaskInternal())
            return HandleSuccess();
        else
            return HandleFailure();
    }

    // overide this to implement the task execution logic
    private virtual bool ExecuteTaskInternal()
    {
        return true;
    }

    // overide this to implement logic for successful completion
    // and return the appropriate success code
    private virtual Status HandleSuccess()
    {
        return Status.Success;
    }

    // overide this to implement the task execution logic
    // and return the appropriate failure code
    private virtual Status HandleFailure()
    {
        return Status.Error;
    }
}

After you've created Task classes to execute your steps, add them to a SortedList in order of execution, then iterate through them checking the staus when the task has completed:

public void processStuff()
{
    Status returnStatus 
    SortedList<int, TaskBase> list = new SortedList<int, TaskBase>();
    // code or method call to load task list, sorted in order of execution.
    try
    {
        foreach(KeyValuePair<int, TaskBase> task in list)
        {
            Status returnStatus task.Value.ExecuteTask();
            if(returnStatus != Status.Success)
            {
                break;
            }
        }
    }
    catch (Exception ex)
    {
        log(ex);
        returnStatus = Status.Error;
    }
    finally
    {
        //some necessary cleanup
    }

    return returnStatus;
}

I left in the error handling as I'm not sure if your trapping errors on task execution or just looking for the exception you were throwing on a particular step failing.

Jeremy Seghi
A: 

Expanding on the interface approach above:

public enum Status { OK, Error, Warning, Fatal }

public interface IProcessStage {
    String Error { get; }
    Status Status { get; }
    bool Run();
}

public class SuccessfulStage : IProcessStage {
    public String Error { get { return null; } }
    public Status Status { get { return Status.OK; } }
    public bool Run() { return performStep1(); }
}

public class WarningStage : IProcessStage {
    public String Error { get { return "Warning"; } }
    public Status Status { get { return Status.Warning; } }
    public bool Run() { return performStep2(); }
}

public class ErrorStage : IProcessStage {
    public String Error { get { return "Error"; } }
    public Status Status { get { return Status.Error; } }
    public bool Run() { return performStep3(); }
}

class Program {
    static Status RunAll(List<IProcessStage> stages) {
        Status stat = Status.OK;
        foreach (IProcessStage stage in stages) {
            if (stage.Run() == false) {
                stat = stage.Status;
                if (stat.Equals(Status.Error)) {
                    break;
                }
            }
        }

        // perform final processing
        return stat;
    }

    static void Main(string[] args) {
        List<IProcessStage> stages = new List<IProcessStage> {
            new SuccessfulStage(),
            new WarningStage(),
            new ErrorStage()
        };

        Status stat = Status.OK;
        try {
            stat = RunAll(stages);
        } catch (Exception e) {
            // log exception
            stat = Status.Fatal;
        } finally {
            // cleanup
        }
    }
}
jheddings
Seeing your changes to pass data between processing steps, this might not work as is. Hopefully it will get you started.
jheddings
A: 

I'd advise on some refactoring of the steps into seperate classes, after all, your class should only have a single responsibility anyway. I think it sounds like it's responsibility should controlling the running of the steps.

Michael Baldry