views:

634

answers:

14

I am experimenting with different areas of C# and refactoring best practices/patterns.

As can be seen the Validate method below has 3 child validation methods.

Is there a way to redesign this method/refactor it so that the if statement are remove? (possibly using Delegate?).

Also what general code standard improvements would you suggest?

public bool Validate()
{            
     bool validDump;

     validDump = ValidateRecordIdentifiers();
     if (!validDump)
     {
         LogLogic.AddEntry(LogLogic.GetEnumDescription(
              LogMessages.StatusMessages.JobValidationFailed));
         return false;
     }

     validDump = ValidateTotals();
     if (!validDump)
     {
         LogLogic.AddEntry(LogLogic.GetEnumDescription(
              LogMessages.StatusMessages.JobValidationFailed));
         return false;
     }

     validDump = ValidateRecordCount();
     if (!validDump)
     {
         LogLogic.AddEntry(LogLogic.GetEnumDescription(
              LogMessages.StatusMessages.JobValidationFailed));
         return false;
     }

     LogLogic.AddEntry(LogLogic.GetEnumDescription(
          LogMessages.StatusMessages.JobValidationPassed));
     return true;
}
+1  A: 

You can take a look at Validation Application Block and Code Contracts

Dzmitry Huba
+2  A: 

You could modify your validate methods so that they take in the LogLogic parameter and add an entry themselves for failing.

They could still return a boolean value, and this could be used to keep your return as soon as possible.

return ValidateRecordIdentifiers(LogLogic) 
    && ValidateTotals(LogLogic) 
    && ValidateRecordCount(LogLogic);
Matthew Steeples
But then where does the successful logging case get performed?
MattMcKnight
Also I may be wrong, but wouldn't this create a dependency and thus couple the 3 validate methods to the param being passed.
N00b
Were I in your position, I would worry more about fully understanding the language before I worried about managing dependencies.
Erik Forbes
@Erik - I was merely applying a concept a I read about, to a proposed solution. Is that not the basis of all learning?
N00b
I didn't mean that you should never learn about dependencies, as it's an important topic - just that it's more important for you to get the fundamentals down first.
Erik Forbes
There is no need for a parameter for the method when the method does the logging. You could throw in a boolean to enable/disable logging. However I wouldn't put too much logic in a simple validation method.
Zyphrax
+17  A: 
    bool valid = false; 
       if(ValidateRecordIdentifiers() && ValidateTotals() && ValidateRecordCount())
        {
          valid = true;
        }


/******AN Alternate Suggestion for the above code********/
    bool valid =  ValidateRecordIdentifiers() && 
                  ValidateTotals() && 
                  ValidateRecordCount();
/*******End Alternate Suggestion*************/


   var statusMessage = (valid) ? 
       LogMessages.StatusMessages.JobValidationPassed :
       LogMessages.StatusMessages.JobValidationFailed

   LogLogic.AddEntry(LogLogic.GetEnumDescription(statusMessage));

   return valid;

See short circuiting: http://msdn.microsoft.com/en-us/library/2a723cdk%28VS.71%29.aspx

Kevin
But the return false would occur very late.
N00b
No, it would return after one of them fails.
Kevin
You forgot logging when validate passed.
Iamamac
I don't understand the comment about the late return. I would add the successful logging entry to the valid=true; block.
MattMcKnight
I think what he was thinking was that it would have to evaluate all three expressions before returning.
Kevin
Thats right Kevin, my mistake you are correct, simple answer from you.
N00b
Crikey, you'd think a guy with nearly 20K rep would know how to edit code so as not to leave ugly scroll bars.
Kyralessa
Dave
This is going to be much harder to debug and trace than the original. It would be better in lisp (where debugging is parenthesis oriented) but not in c# (where debugging is statement, not expression oriented). An artifact of the IDE, true, but it is an important consideration.
Larry Watanabe
@Larry have you discovered the F11 key yet? It's great! :)
fearofawhackplanet
@fearofawhackplanet it's a pain to step into a function when you can find it much faster by continuing to step through the same function. Also, he should really be logging different messages for different failure conditions, which you can't do easily within an expression.
Larry Watanabe
+1 for avoiding unneccessary code duplication.
Doc Brown
I'm glad this answer made it, I upvoted you Kevin ;-)
Dave
The alternate solution should be THE solution in my opinion. Changing booleans with an if-statement is kinda ugly. I would also create an LogLogic.AddEntry overload in the logger to accept a StatusMessages enum.
Zyphrax
+2  A: 
public bool Validate()
{
    return Validate(ValidateRecordIdentifiers, ValidateTotals, ValidateRecordCount);
}

public bool Validate(params Func<bool>[] validators)
{
    var invalid = validators.FirstOrDefault(v => !v());
    if (invalid != null)
    {
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed));
        return false;
    }
    LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed));
    return true;
}
Darin Dimitrov
+1  A: 

You could do something simple like this:

bool validDump;
string message;
if ((!ValidateRecordIdentifiers()) ||
    (!ValidateTotals()) ||
    (!ValidateRecordCount()))
{
    message = LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed);
}
else
{
    message = LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed);
    validDump = true;
}
LogLogic.AddEntry(message);
return validDump;
Justin R.
+1  A: 

This looks to me like a case for structured exception handling. It looks like an exception condition that you are handling in the sense that something invalid has been input, and it results in abandoning the process. Have you considered using try/catch in the parent function and throw within the child functions to handle this?

Example:

public bool Validate()
{
   try
   {
      ValidateRecordIdentifiers();
      ValidateTotals();
      ValidateRecordCount();
      LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed));
      return true;
   }
   catch (ValidationException ex)
   {
      LogLogic.AddEntry(ex.status);
      return false;
   }
}

class ValidationException : ApplicationException
{
   public readonly LogMessages.StatusMessages status;
   ValidationException(LogMessages.StatusMessages status)
   {
      this.status = status;
   }
}

void ValidateRecordIdentifiers()
{
   if (bad)
      throw new ValidationException(LogMessages.StatusMessages.JobValidationFailed);
}

void ValidateTotals()
{
   if (bad)
      throw new ValidationException(LogMessages.StatusMessages.JobValidationFailed);
}

void ValidateRecordCount()
{
   if (bad)
      throw new ValidationException(LogMessages.StatusMessages.JobValidationFailed);
}

Edit: I generally don't like to use exception handling for errors that are not immediately reported out to the UI because exception handling can be costly, and excessive exception throwing can make the application harder to debug if you're trying to find real exception cases among a bunch of exceptions that aren't really "exceptional". But depending on your specific case, it may be appropriate. Just use with caution.

BlueMonkMN
+2  A: 

The first thing that jumps out is duplication: LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed));

So I'd look to collapse it into something like:

public StatusMessages Validate() {

  LogMessages.StatusMessages status = LogMessages.StatusMessages.JobValidationFailed;

  if( ValidateRecordIdentifiers() && ValidateTotals() && ValidateRecordCount())
    status = LogMessages.StatusMessages.JobValidationPassed;

  LogLogic.AddEntry(status.ToString());

  return status;
}
Jeffrey Knight
You cannot override the ToString method on an enum. So you still need the GetEnumDescription. Or remove ToString all together and feed the method parameters of the StatusMessages type.
Zyphrax
+1  A: 

Maybe:

public bool Validate() 
{

        if (ValidateRecordIdentifiers() && ValidateTotals() && ValidateRecordCount())
        {
           LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed));
           return true;
        }

        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed));

        return false;
    }
magnus
+2  A: 

You are writing the same error message regardless of which validation function fails. It might be more helpful to log a specific error message in each case. Otherwise you can rewrite what you already have much simpler:

if (ValidateRecordIdentifiers() && ValidateTotals() &&  ValidateRecordCount())
{
    LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed));
    return true;
}
LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed));
return false;
fearofawhackplanet
+2  A: 

Framework:

class Validator
{
    Func<bool> validatorDelegate;
    Action failDelegate;

    public Validator(Func<bool> v, Action fail)
    {
        validatorDelegate = v;
        failDelegate = fail;
    }

    public bool Validate()
    {
        bool rc = validatorDelegate();
        if (!rc) failDelegate();
        return rc;
    }
}

class ValidatorCollection : List<Validator>
{
    Action successDelegate;
    Action failDelegate;

    public ValidatorCollection(Action failDelegate, Action successDelegate)
    {
        this.successDelegate = successDelegate;
        this.failDelegate = failDelegate;
    }

    public bool Validate()
    {
        var rc = this.All(x => x.Validate());
        if (rc) successDelegate();
        return rc;
    }

    public void Add(Func<bool> v)
    {
        this.Add(new Validator(v, failDelegate));
    }
}

Usage:

class test
{
    public bool Validate()
    {
        return new ValidatorCollection(
            FailAction,
            SuccessAction)
        {
            valTrue,
            valTrue,
            valFalse
        }.Validate();
    }

    public void FailAction()
    {
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed));
    }

    public void SuccessAction()
    {
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed));
    }

    public bool valTrue()
    {
        return true;
    }
    public bool valFalse()
    {
        return false;
    }
}
Aviad P.
I wrote classes similar to that for my validation framework, The code becomes very finite and highly readable but man debugging it can be a pain, I think i might need to implement an optional logging dependency that will roll out every single delegate it checks in text and the boolean result of it.
Chris Marisic
+1  A: 

There's a number of different ways to write this but your method is short and readable. The suggestions posted so far are, imo, much less readable and harder to debug (where would you set a breakpoint?). I would leave this method as is and look for other refactoring opportunities.

Jamie Ide
theres nothing wrong with the OPs solution as a starting point, but theres at least 10 lines of completely redundant code in there. If he wants to learn to think/write code more clearly, there are some great answers here showing different approaches. And I don't think any of them are "less readable and harder to debug"
fearofawhackplanet
... actually I take that back having just read Aviad's post :)
fearofawhackplanet
@fearpfawhackplanet, not nice! It took me 10 minutes to write that framework :)
Aviad P.
No argument there. :-) And I think the accepted answer is a good one although it's functionally equivalent to the original. The more experienced I get the more I value simplicity and readability over clever solutions. I try not to approach refactoring as "code golf" or an opportunity to replace stable code with a more clever solution.
Jamie Ide
I use an approach similar to @Aviad's in production right now. It IS hard to debug but after I hammered out initial bugs in my design of it is completely smooth sailing and lets you implement complex validation logic branches easily and not have 8 billion if statements.
Chris Marisic
A: 
public bool Validate()
{
 return LogSuccess(
  new[] {ValidateRecordIdentifiers, ValidateTotals, ValidateRecordCount }
  .All(v=>v()));
}

private bool LogSuccess(bool success)
{
 LogLogic.AddEntry(LogLogic.GetEnumDescription(success
   ? LogMessages.StatusMessages.JobValidationPassed
   : LogMessages.StatusMessages.JobValidationFailed
 );
 return success;
}
George Polevoy
What's going on with `.All(v=>v()));`?
Chris Marisic
@Chris - The All method performs an operation on all items in the list and only returns true if the result of all of the operations returned true (it stops on the first false result it encounters). So first he creates an array of methods. Executes all of the methods with All and thus only returns true when all of the methods returned true.
Zyphrax
George Polevoy
A: 

Value readability above all else (well, as long as it is in the same ballpark efficiency).

About the only changes I would make is to eliminate the unneeded variable, and use the function call in the conditional, and replace the ! operator with == false. This is easier to see for aging programmers like myself with bad eyesight :)

As implied by the comment of another poster, it is better to make the function read InvalidXX instead, to avoid using negation or == false and for better readability.

Also, as far as combining all the conditionals into a single "AND" statement, I would do that in lisp, but not in c#, because it will making debugging and tracing harder.

In particular, you probably don't want to put the same error message for each case - you should have a different one for each case so you know exactly what happened. Combining all cases into a single expression won't allow you to do this.


public bool Validate() {

     if (ValidRecordIdentifiers() == false)        { 
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed)); 
        return false; 
    } 

    if (ValidTotals() == false)        { 
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed)); 
        return false; 
    } 

     if (ValidateRecordCount() == false)        { 
        LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationFailed)); 
        return false; 
    } 

    LogLogic.AddEntry(LogLogic.GetEnumDescription(LogMessages.StatusMessages.JobValidationPassed)); 
    return true; 
} 
Larry Watanabe
+1 for emphasising readability over other factors. However, boolean comparisons to `false` can be avoided, don't you think?
CesarGon
Agree - better to make the functions read InvalidXX.
Larry Watanabe
Larry, I do not think your (or the original) code ís better readable (for one who is used to the short-circuit idiom). And it is definitely not better maintainable, since it misses the DRY principle. Duplicate code means code that is harder to refactor. I would favor Kevins approach.
Doc Brown
It would only be considered readable if readability meant: 'i like reading, and there is planty to read, so it's very readable!'.
George Polevoy
Are you kidding? You're really suggesting that it's better to duplicate all that code. That's plain awful. And it's *not* easier to read - your eyes have to scan all the duplicated code to realize it's doing the same thing. As described in most of the answers, a short circuiting if statement would usually be the way you'd handle this.
Phil
Note, if the code blocks where different, then separate if statements might make sense, but not as it stands with the same duplicate code block.
Phil
Exactly my point - there should be different messages logged for the different errors, so code blocks should be different as well.
Larry Watanabe
Then your comment shouldn't be criticizing others for "being less easy to understand". Your comment should be that different errors should be generated. Which is any case is really more a challenge based on the original poster's functionality, not refactoring of the if statement. If however, he does want the same functionality duplication should be avoided. Short circuiting of if statements is essential basic knowledge.
Phil
A: 

Your function does two things: validation and logging. You could separate them like this. This also lets you log these errors differently if you ever decide to do this.

public bool ValidateAndLog()
{
    LogMessages.StatusMessages result=Validate();
    LogLogic.AddEntry(LogLogic.GetEnumDescription(result));
    return result==LogMessages.StatusMessages.JobValidationPassed;
}

private LogMessages.StatusMessages Validate()
{
    //of course you can combine the next three ifs into one 
    if (!ValidRecordIdentifiers())
        return LogMessages.StatusMessages.JobValidationFailed;
    if (!ValidateTotals())
        return LogMessages.StatusMessages.JobValidationFailed;
    if (!ValidateRecordCount())
        return LogMessages.StatusMessages.JobValidationFailed;
    return LogMessages.StatusMessages.JobValidationPassed;
}
yu_sha