Builder Part 3

In the last two articles I covered the high-level architecture of my Builder library. I then went into detail, describing how the library did what it does. I finished by promising that I would lay out several different strategies for using the Builder library. Let’s do that now.

Using the Builder

How you might use the Builder really depends on what you’re trying to build and what you’re building it from. For me, I usually put any kind of shared resource in a Provider and then load it at build time using the Builder and a concrete Setup class. For adding the Setups to the Builder, I prefer to put that code into an extension class, just to make everything prettier.

If you’ve ever used any of the builders in Microsoft’s NUGET packages then you’ve seen a pattern similar to the one I try to follow: Create a builder, use one or more extension methods on that Builder to add the Setups, then use the methods on the concrete Setup object to configure the Setup, then call the Build method on the builder.

Here is some example code:

It looks like a lot going on, and maybe it is, but let’s work through it together …

We start by creating a product class, MyProduct. That’s so we’ll have something for a Builder to make.

Afterwards, we create a concrete Builder class, MyBuilder. Technically, we don’t need the concrete Builder class, but in practice I almost always create one, and here’s why, it makes it easier to create the extension methods I’ll need later, to add various Setup types to the Builder. If I never created concrete Builder classes, I’d have to hand those methods off the IBuilder type. That works, and I do that when I want a Setup type to work with ANY kind of Builder, but it would get busy if we did that all the time.

The MyProvider and MySetup types are a matched set that I’ve created here for demonstration purposes. Neither one actually does anything, other than illustrate how to create a Provider and Setup for a project. I did include a “Name” property on the Setup, just to show how properties typically happen for the Setups.

The MyBuilderExtensions class is the extension class that I alluded to earlier. Using this approach, we can narrow down the types of Builders that our Setup (and therefore, our Provider) will work with. It’s also cleaner to put the code for adding the Setup to the Builder in another method, to keep it out of the callers code. Of course, just because we use an extension method in this way doesn’t prevent an enterprising young developer from hooking everything up themselves, and doing it incorrectly. You’ll still need to check for that, if it matters to you.

The only thing left is the main part of the example, where we actually use the builder to create the product. Look carefully at that code. There’s three lines of code there. Four, if you count the SetName call as a separate line. Those three lines of code represent the steps your customers will have to deal with, in order to build anything you offer that’s based on this Builder. 3 lines of code: (1) create a builder, (2) add a setup (and configure it), (3) create the product.

Also, bear in mind, you didn’t have to write a custom ConcreteBuilder class to create the MyProduct object. There was no custom Director class to write either. Also, in the real world, the MyProduct class would likely need to have many different kinds of resources, algorithms, repositories and more attached to it at runtime. Think about what a pain it would be to go back and rewrite that ConcreteBuilder class everytime your MyProduct class needed a new type of whatsit associated with it.

That’s why I created a reusable Builder.

Real World Use

So, the example I gave is a bit fuzzy, I admit. I’m probably not the best impromptu example writer in the world. However, I do use this Builder extensively in my various NUGET packages, so, feel free to go look at that code. Everything is on GITHUB at: http://www.github.com/codegator

A few of the packages that use this Builder are:

  • * CG.Linq, CG.Linq.Mongo
  • * CG.Linq.EntityFramework
  • * CG.Linq.EntityFramework.InMemory
  • * CG.Linq.EntityFramework.SqlLite
  • * CG.Linq.EntityFramework.SqlServer.
  • * CG.IO
  • * CG.IO.FileSystem
  • * CG.IO.ZipFile
  • * CG.Plugin
  • * CG.Plugin.FileSystem
  • * CG.Plugin.Reflection
  • * CG.Process
  • * CG.Process.Console
  • * CG.Process.Windows

Advanced Uses

The Builder also supports what I consider to be two kinds of advanced uses. (1) small hierarchies of Providers, and (2) Builders embedded within Products.

I’ve encountered the first scenario in my CG.IO package, where I have an Archive abstraction that leverages the builder’s Provider model in order to support various back end options, such as databases, files, etc. I also have Providers that support encryption / decryption in the IO, between the Archive and it’s Providers. Thing is though, I don’t usually need the cryptography support, so, for those times when I don’t need it, I don’t see any reason to pay the overhead for it. The way I solved that was to used the crypto Providers as children of the main storage Providers. I added code to the storage Providers to check for the presence of the crypto Providers, and if found, a crypto stream from the crypto Provider is wired into the mix.

Here, it’s probably easier just to demonstrate in code:

In the code, the call to AddFileUnitOfWorkProvider() injects the setup for a file based storage provider. The call to AddCryptoProvider() adds the crypto Provider as a child of the storage Provider. Something like this wouldn’t be possible without the child collections on the Provider and Setup abstractions.

The second scenario happens much more often, at least in my NUGET packages. Here is a code sample:

This code uses a builder to create an instance of MyProduct. Notice that MyProduct uses a LogBuilder to create an ILog instance, inside the Product’s OnInitialize override. Stop to consider though, how the embedded Builder gets configured … That’s right, through the parent Builder! How does that work? Notice in the Main method, that we call AddConsoleLogProvider() on the Builder. That call adds a setup for a console log provider to the builder. When the Builder method is called, the MyProduct instance is created, but, the Builder also creates the console log Provider and adds it to the Product’s Providers collection, behind the scenes. That means the Provider is available to the MyProduct object at runtime. That means the embedded log Builder, inside MyProduct, can use the existing log Provider by creating the Build with the constructor override that accepts a Product reference.

Using this approach, it’s possible to stack Builders N levels deep and still configure everything quickly and easily from the top level Builder.

 

The code for this article is part of my NUGET package CG.Builder, which can be downloaded for free at https://github.com/CodeGator/CG.Builder

The source code for the CG.Builder package lives on Github and can be obtained for free at

https://www.nuget.org/packages/CG.Builder

That’s about it. I hope this article has been helpful. Have fun with the code!