Guard Clauses
In my opinion your code will become more complex with more requirements. In additional to this you mus implement more and more checks, e.g. against null parameters or out of range values.
So before an method execute some neccessary logic, you will (maybe) implement some excessive checks against the method parameters, to ensure the correctnes of these. This can be produce a huge amount of "Payload". To keep this simple you can use the guard clauses.
This is a simple chacke the immediatley exists the function, either with a return statement or an execption. This will reduce your code complexitiy and increase the readabililty of this.
Let assume the following example code
public void Subscribe(User user, Subscription subscription, Term term)
{
if (user != null)
{
if (subscription != null)
{
if (term == Term.Annually)
{
// subscribe annually
}
else if (term == Term.Monthly)
{
// subscribe monthly
}
else
{
throw new InvalidEnumArgumentException(nameof(term));
}
}
else
{
throw new ArgumentNullException(nameof(subscription));
}
}
else
{
throw new ArgumentNullException(nameof(user));
}
}
This way you will tpye if you want to checkt the parameters of its type, values and so on. You'll see that it very hard to understand where the actuall logic code is and wich parameters are allowed. Sure you can write it into the paremter definitions (in the summy tags), but this will not garantee you the correctness.
So first let's refactor this code. First of al ew invert the logic of the if statements and throwing the exceptions inside of the if statements, this is the result:
public void Subscribe2(User user, Subscription subscription, Term term)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (subscription == null)
{
throw new ArgumentNullException(nameof(subscription));
}
if (term == Term.Annually)
{
// subscribe annually
}
else if (term == Term.Monthly)
{
// subscribe monthly
}
else
{
throw new InvalidEnumArgumentException(nameof(term));
}
}
So there are multiple checks against null, aht eht value of the term will also be checked. So in fact I will use the DRY
( Don't repeat yourself) principle abd creating a helper method:
public static class Guard
{
public static void AgainstNull(object argument, string argumentName)
{
if (argument == null)
{
throw new ArgumentNullException(argumentName);
}
}
public static void AgainstInvalidTerms(Term term, string argumentName)
{
// note: currently there are only two enum options
if (term != Term.Annually &&
term != Term.Monthly)
{
throw new InvalidEnumArgumentException(argumentName);
}
}
}
These helper guard methods can then be called without the need to even include any if statements in the calling function, since if an exception occurs it will bubble up and out of the original function. Now the original function can be updated to look like this:
public void Subscribe3(User user, Subscription subscription, Term term)
{
Guard.AgainstNull(user, nameof(user));
Guard.AgainstNull(subscription, nameof(subscription));
Guard.AgainstInvalidTerms(term, nameof(term));
if (term == Term.Annually)
{
// subscribe annually
return;
}
// subscribe monthly
}
You will now be free to add new Guard methods for any other common cases. I created a Nuget Package (opens new window)for the most common scenarios, you will find the source code here (opens new window)on my github.