Coding Better: Programming From the Outside In for Fluent Interfaces. Part IV - A Real World Application

In my previous articles I talk about coding from the outside in which is the technique I will be using in the rest of this article.  I'll be skipping the details of the coding technique and just show the results so if you want more details on exactly how the code was generated here are the links:

Now let's look at using this approach to build a library to abstract parameter assertions.

Parameter assertions are most often plumbing details that can distract us from the domain logic within a class, especially if they take many lines of code.  When reading a method to do code maintenance you will always have to read them to see if they pertain to what you are looking for.  More often than not, parameter assertion code is repeated almost verbatim in other methods.  It would be a good idea to prove a layer of abstraction around this repeated code and make it concise, readable, consistent and easy to implement.  This makes parameter assertions a perfect candidate for abstraction through a fluid interface.

Debug.Assert() only is compiled for debug builds and allows us to halt the code if there is a violation with the thread's stack intact  making debugging easier.   It is similar to a breakpoint except that when we hit it while debugging code execution stops and we get a message box.  If we want our methods to be easy to debug, we should add these assertions before explicitly throwing an exception in order to make the code less expensive to consume from another method or another library because it will be easier to understand exactly why a particular exception was thrown.

public void Go(String value)
{
    #region Validation
   
    // Make method easier to code against
        Debug.Assert(String.IsNullOrEmpty(value), "value parameter can not be null or empty");
 
    if(String.IsNullOrEmpty(value))
        throw new ArgumentNullException("value");
 
    #endregion
 
    // Everything is ok... we can start implementation here
}

In unit test projects, we probably don't want to have to see each message box for the Debug assertions because we are explicitly forcing exceptions to happen in order to pin them down.  The debug message box can be suppressed with the following configuration setting which we'll put in our unit test project.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <assert assertuienabled="false"/>
  </system.diagnostics>
</configuration>

The way the assertion code was implemented (above) is not really a big deal for one parameter, but imagine if we have up to four or five assertions in hundreds of public/protected methods which we want to provide assertions on.  That's a lot of repeated code, a significant amount of unnecessary code debt, and a lot to read through at the top of each method which will slow down any maintenance that has to be done on the method.  All this can really add up as our code base grows and will make maintenance expensive and not very fun.

Imagine if we could replace those 3 lines of code with something like:

Assert.Parameter("value1", value1).IsNotNullOrEmpty();


Take it one step further; we need all sorts of different tests for parameters as our methods are called.  With that in mind, here is the first draft of our code sketch with some of the tests that we'll use for building our API:

public void Go(String value1, Int32 value2)
{
    #region Validation
 
    AssertParameter.With("value1", value1)
        .IsNotNull()
        .IsNotNullOrEmpty()
        .Matches("Starts with a non-space character", @"$[^\s]"); // regex tests
 
    AssertParameter.With("value2", value2)
        .IsGreaterThan(10)
        .IsLessThanOrEqualTo(20);
 
    AssertParameter.With("value2", value2)
        .Is("Multiple of two.", p => p % 2 == 0) // lambda tests
        .IsBetween(5, 50); 
    #endregion
 
    // ok.. it is now safe to continue
}

How much more concise would this be than writing all this code manually every time we need to check parameters?  Having these assertions is a good idea to do in every public/protected method if we want a reliable code base.

So let's take our sketch (above) and start building the API from the outside in.  First we'll generate the AssertParameter class.                    

public static class AssertParameter
{          
}

Next, we'll build the With() method and decide what type we would like returned from the method.  Let's try a general ParameterAsserter<T> that we can use as a base class to derive other classes from.

public static class AssertParameter
{
    public ParameterAsserter<T> With<T>(string parameterName, T parameterValue)
    {
        throw new NotImplementedException();
    }
}

Next, we'll generate the object returned from the With() method:

public class ParameterAsserter<T>
{
}

Now that we have a bit of the API together, I'm beginning to see a real big problem going down this road! 

Can you see it?

We are going to run into a problem because the method chain operating on a string type needs to return same linguistic element and can't pass back the base type ParameterAsserter<T> because we'll lose any derived classes methods that are especially for strings (like regular expression tests).  Here's where we have to go back and rethink our model a bit.

We really don't want any repeated code in our final product so let's change the Parameter asserter to a utility class.

internal static class ParameterAsserter
{
    public static void IsNotNull(String parameterName, Object value)
    {
        throw new NotImplementedException();
    }
    public static void Is<T>(string testDescription, Predicate<T> test)
    {
        throw new NotImplementedException();
    }
}

And let's make the AssertParameter class have an overloaded "With()" method so that we control the types being built and can retain a return type of the same class that performs the method chaining so we don't get a reference for a base class and loose our method calls.

public static class AssertParameter
{
    public StringParameterAsserter With(string parameterName, String parameterValue)
    {
        throw new NotImplementedException();
    }
}

Now we'll stub out the class and method chain calls for the StringParameterAsserter, our type-specific class used for method chaining.

public class StringParameterAsserter
{
 
    public StringParameterAsserter IsNotNull()
    {
        throw new NotImplementedException();
    }
    public StringParameterAsserter IsNotNullOrEmpty()
    {
        throw new NotImplementedException();
    }
    public StringParameterAsserter Matches(string testDescription, string regexMatchCriteria)
    {
        throw new NotImplementedException();
    }   
}

Having a general assertion would be good that could basically test anything with a lambda expression. We'll update the sketch to add an assertion via a lambda expression against the string.

AssertParameter.With("value1", value1)
    .IsNotNull()
    .IsNotNullOrEmpty()
    .Matches("Starts with a non-space character", @"$[^\s]"), // regex tests
    .Is("Five characters long", s=>s.Length==5);

Generating the methods from our sketch, we get the following result.

public class StringParameterAsserter
{
 
    public StringParameterAsserter IsNotNull()
    {
        throw new NotImplementedException();
    }
    public StringParameterAsserter IsNotNullOrEmpty()
    {
        throw new NotImplementedException();
    }
    public StringParameterAsserter Matches(string testDescription, string regexMatchCriteria)
    {
        throw new NotImplementedException();
    }
    public StringParameterAsserter Is(string testDescription, Predicate<String> test)
    {
        throw new NotImplementedException();
    }   
}

Now let's start working on our utility class and define how the StringParameterAsserter class should interact with it leaving the NotImplementedExceptions in place until we start writing unit tests.

I would rather dive in a bit deeper at this point to make sure we don't have any problems that could make us have to start over.  Something to keep in mind as we go is that if we keep the methods small enough they will be inlined when compiled in release build.

public class StringParameterAsserter
{
 
    private readonly String
        m_ParameterName,
        m_ParameterValue;
 
    public StringParameterAsserter IsNotNull()
    {
        throw new NotImplementedException();
 
        ParameterAsserter.IsNotNull(m_ParameterName, m_ParameterValue);
        return this;
    }
    public StringParameterAsserter IsNotNullOrEmpty()
    {
        throw new NotImplementedException();
 
        ParameterAsserter.IsNotNullOrEmpty(m_ParameterName, m_ParameterValue);
        return this;
    }
    public StringParameterAsserter Matches(string testDescription, string regexMatchCriteria)
    {
        throw new NotImplementedException();
        ParameterAsserter.Matches(m_ParameterName, m_ParameterValue, testDescription, regexMatchCriteria);
        return this;
 
    }
    public StringParameterAsserter Is(string testDescription, Predicate<String> test)
    {
        throw new NotImplementedException();
        ParameterAsserter.Is(m_ParameterName, m_ParameterValue, testDescription, test);
        return this;
    } 
}

Now we'll stub out the ParameterAssert class and its methods, making some generic for better reuse and making IsNull() take an object.

internal static class ParameterAsserter
{
    public static void IsNotNull(String parameterName, Object parameterValue)
    {
        throw new NotImplementedException();
    }
    public static void IsNotNullOrEmpty(String parameterName, String parameterValue)
    {
        throw new NotImplementedException();
    }
    public static void Matches(String parameterName, String parameterValue, string testDescription, string regexMatchCriteria)
    {
        throw new NotImplementedException();
    }
    public static void Is<T>(String parameterName, String parameterValue, string testDescription, Predicate<T> test)
    {
        throw new NotImplementedException();
    }
}

So far so good.  Now we'll go back to sketch and update it a bit since we have a better understanding of the domain.

public void Go(String value1, Int32 value2)
{
    #region Validation
 
    AssertParameter.With("value1", value1)
        .IsNotNull()
        .IsNotNullOrEmpty()
        .Matches("Starts with a non-space character", @"$[^\s]") // regex tests
        .Is("Five characters long", s=>s.Length==5);
 
    AssertParameter.With("value2", value2)
        .IsGreaterThan(10)
        .IsGreaterThanOrEqualTo(5)
        .IsLessThan(50)
        .IsLessThanOrEqualTo(20)
        .IsBetween(5,50)
        .Is("Multiple of two.", p => p % 2 == 0); // lambda tests            
 
    #endregion
 
    // ok.. it is now safe to continue
}

All the assertions on the integer seem to be something we could do off of anything implementing IComparable. So maybe building a ComparableParameterAsserter<> is the way to go.

There is only one way to find out, so let's try going down this path and see where it gets us:

public static class AssertParameter
{
    public static StringParameterAsserter With(string parameterName, String parameterValue)
    {
        throw new NotImplementedException();
    }
 
    public static ComparableParameterAsserter<Int32> With(String parameterName, Int32 value)
    {
        throw new NotImplementedException();       
    }
}

Here we'll stub out the class and generate all the methods with an outside-in approach.

public class ComparableParameterAsserter<T> where T : IComparable<T>
{
    private readonly T
        m_ParameterValue;
 
    private readonly String
        m_ParameterName;
 
    public ComparableParameterAsserter<T> IsGreaterThan(T value)
    {
        throw new NotImplementedException();
    }
    public ComparableParameterAsserter<T> IsGreaterThanOrEqualTo(T value)
    {
        throw new NotImplementedException();
    }
    public ComparableParameterAsserter<T> IsLessThan(T value)
    {
        throw new NotImplementedException();
    }
    public ComparableParameterAsserter<T> IsLessThanOrEqualTo(T value)
    {
        throw new NotImplementedException();
    }
    public ComparableParameterAsserter<T> IsBetween(T value1, T value2)
    {
        throw new NotImplementedException();
    }
    public void Is(string description, Predicate<T> test)
    {
        throw new NotImplementedException();
    }
}

And now our current sketch builds.

What if we have a parameter that is not a string and does not implement IComparable?  We need to expand our sketch to test for equality which should go into a new class.  Something like a SimpleParameterTest<> class might work as a fallback for types that we don't know about right now and could be defined at the level of the consumer of our library.

Another thing to think about is that we want to add static helper methods off of the AssertParameter class in case only one thing needs to be checked for a parameter in a method.

So we update our sketch again:

public class Car
{
    public String Name { get; set; }
}
 
public class __SKETCH
{
   
    public void Go(String value1, Int32 value2, Car value3)
    {
        #region Validation
 
        AssertParameter.With("value1", value1)
            .IsNotNull()
            .IsNotNullOrEmpty()
            .Matches("Starts with a non-space character", @"$[^\s]") // regex tests
            .Is("Five characters long", s=>s.Length==5)
            .IsNot("Five characters long", s => s.Length == 5)
            .IsEqualTo("something")
            .IsNotEqualTo("something");
 
        AssertParameter.IsNotNull("value1", value1);
        AssertParameter.IsNotNullOrEmpty("value1", value1);
        AssertParameter.Matches("value1", value1, "Starts with a non-spaceCharacter", @"$[^\s]"); // regex tests
        AssertParameter.Is("value1", value1, "Five characters long", s => s.Length == 5);
        AssertParameter.IsNot("value1", value1, "Five characters long", s => s.Length == 5);
        AssertParameter.Equals("value1", value1, "something");
        AssertParameter.NotEquals("value1", value1, "something");
 
 
        AssertParameter.With("value2", value2)
            .IsGreaterThan(10)
            .IsGreaterThanOrEqualTo(5)
            .IsLessThan(50)
            .IsLessThanOrEqualTo(20)
            .IsBetween(5, 50)
            .IsEqualTo(10)
            .IsNotEqualTo(30)
            .Is("Multiple of two.", p => p % 2 == 0) // lambda tests            
            .IsNot("Multiple of two.", p => p % 2 == 0); // lambda tests
 
        AssertParameter.IsGreaterThan("value2", value2, 10);
        AssertParameter.IsGreaterThanOrEqualTo("value2", value2, 5);
        AssertParameter.IsLessThan("value2", value2, 10);
        AssertParameter.IsLessThanOrEqualTo("value2", value2, 5);
        AssertParameter.IsBetween("value2", value2, 10, 30);
        AssertParameter.IsEqualTo("value2", value2, 5);
        AssertParameter.IsNotEqualTo("value2", value2, 5);
        AssertParameter.Is("Multiple of two.", p => p % 2 == 0); // lambda tests            
        AssertParameter.IsNot("Multiple of two.", p => p % 2 == 0); // lambda tests
 
 
        AssertParameter.With<Car>("car", new Car { Name = "VW" })
            .IsNotNull()
            .Is("A VW", c => c.Name == "VW")
            .IsNot("A VW", c => c.Name != "VW")
            .IsEqualTo(new Car())
            .IsNotEqualTo(new Car());
 
        #endregion
 
        // ok.. it is now safe to continue
    }
}

Now we'll stub out the last type (SimpleParameterAsserter<>) and the remaining methods so that we can get back to a place where our sketch builds. Next we'll start building out the unit tests.

After a few more iterations of creating tests, updating the sketch, implementation and making sure everything is commented we are pretty much done.

After all our unit tests have been built and pass here is what it will look like to consume our fluid interface:

If available, the IDE can help make the library somewhat self-documenting through intellisense and by displaying our code comments the library is pretty easy to consume.

image001.gif

image002.gif

image003.gif

 And we end up with concise readable parameter validation code in the end and have less code debt in our consuming project as a result.

public void DoSomething(String actionName, Action action)
{
    #region Validation
 
    AssertParameter
        .With("actionName", actionName)
        .IsNotNullOrEmpty()
        .IsNot("Longer than 50 characters.", s => s.Length > 50)
        .DoesNotMatch("Has whitespace characters", @"\s");
 
    AssertParameter.IsNotNull("action", action);           
 
    #endregion
   
    // code here
 
}

I hope you find this simple library useful and can see how approaching writing code from the outside in can give some nice results that would be difficult to achieve if programming from the inside out.

Until next time,
Happy coding


Similar Articles