Doing It Yourself - Plumbing Driven Development

Wed Jul 04 18

One of the true memes of modern C# development is the fluent API. That's where you go

public class Configutator
{
  public Configurator PlumbInSocketA(string option)
  {
    // do some stuff
    return this;
  } 

  public Configurator PlumbInSocketB(string option)
  {
    // do some stuff
    return this;
  } 
}

And the idea is that the calling code looks like this

var conf = new Configurator().PlumbInSocketA("").PlumbInSocketB("");

The reason you'd do this is if your configurator was a big complicated program with more options than a boeing 747 and the reasonable default for it is to provide you with nothing. For example a program's context object has an exception handler and a logger (which may not be the same thing for once) and you can "plumb in" your "bespoke" logger or serilog. You always want Serilog.

Your Http server may need to "plumb in" two static file servers (like on this website) one for Kestrel to use with user data, and one for pure static calls like the giant background images I use. http://jhray.com/static will be served through nginx with a quick pipeline, whereas http://jhray.com/images will be served through Kestrel.

I've used this at work when making my own URL Get API. I'll have a bunch of URL params and depending on where I am in the client program there are some I need and some I don't. My code then looks like this

var request = new PowerBiRequest("ReportName");
request.DisplayParameters(params)
  .Filters(filters)
  .Role(role)
  .Go();

The useful thing being that when I had a requirement to extend this code to add the Role to the web parameters, I could simply add the .Role method to the pipeline where applicable, and leave it out otherwise. Nifty!

Of course this could be achieved like this as well for a simple string builder class like a URL api

var request = new PowerBiRequest("ReportName")
{
  Filters = filters,
  Role = role,
};
request.Go();

but for things more complicated like Mocking DI and Asserting stuff you'll need to define behaviours, which looks less trivial in Fluent API

var myMockedThing = new Mock<Thing>();
myMockedThing
  .Options(/*lambda that does stuff, calls methods on Thing*/)
  .Then(/*define a resultant action from options*/)
  .ButIf(/*a problem with then code which should throw exception*/)
  .Returns(/*define basic return value if needed*/)
  .Is(/*pass or fail an Assert test with an if statement*/);

As with everything Moq I don't know how the fck this works or really what it does, but when it decides the test passes I'm happy. I don't really think I have an opinion either way on which style I like to read/maintain. I really hate using the word "plumbing" when referring to programming though.