Software Mechanics
Why do we even have that lever?

End of the Deconstruction

February 28, 2008 18:01 by Chris

I'd like to thank everyone for the great reception for my Deconstructing ObjectBuilder series. It's good to know I was filling a gap.

However, I'm currently thinking that I'm not going to post the last segment on using OB to wire up to the event broker. I was editing the text for the post last night, and I realized that it needed serious work to update to OB2. Serious enough to take at least a couple of days. And, quite honestly, I think I've got more important things to write about.

So I think I'm going to call an end to the Deconstructing ObjectBuilder series. And intead start a new one, on Unity, extensibility, and how Unity uses OB2. The EventBroker example I've been using is actually included as one of the Unity quickstarts, so I don't even need to update that code for the container. Wink

I have uploaded all the code from the DeconstructingObjectBuilder series so far. This code even works, unlike some of the blog posts with last minute typos in it. Feel free to play with it. You'll need to download and compile the OB2 sources first. But to be honest, with Unity out there (which has made some significant changes to OB2) I'm not sure how much effort that's worth right now. You can tell me.

Thanks for reading, now on to something (sorta) new!

DeconstructingObjectBuilder.zip (37.93 kb)


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Deconstruction ObjectBuilder - Wiring, part 1

February 11, 2008 17:26 by Chris

I've gotten some feedback that these OB articles are a little long. I'm going to try chopping them up into smaller pieces and see how it goes. Please let me know!

Wiring Objects Together

We've looked at using ObjectBuilder to create objects. But creating an object is only part of the job. Good OO designs use many collaborating objects. Hooking these objects together can take a lot of code, and this wiring code is often hard to maintain. ObjectBuilder can be used to automate this wiring as part of the construction process, or even on objects that already exist.

This kind of wiring is at the core of the Dependency Injection pattern, which we'll get to in just a few more installements.

Wiring Events

As an example of the kinds of object wiring you'd want to automate, I built a simple event broker object. Events are a fundamental part of the .NET object model, and are used throughout the BCL. Events are raised for everything from buttons being clicked to assembly resolution failing. They're a great tool.

But (isn't there always a but?) building event driven systems can be very complex. In order to become an event receiver, your object has to have a reference to the event source. This isn't an issue on a simple dialog box, but in a larger app this can result in spaghetti very quickly. For example, consider implementing the basic cut, copy, and paste operations on a form with a bunch of text boxes and a menu. Each text box would need to hook up to the menu items. But wait - these events also need to hook up to the clipboard keyboard shortcuts. But wait - the keyboard shortcuts are sent to the text boxes themselves, which means that the text boxes actually produce as well as respond to these events. This means (at first glance) that every text box needs to hook up to events on every other text box. Ick.

There's an old adage in computer science: "Every problem can be solved with another layer of indirection." That's what the event broker provides . Instead of registering with every possible producer of an event, you instead register with only one - the broker. The broker takes care of the details of routing events, regardless of how they're generated, to the objects that care about them.

As a side note: This event broker is intended as a demonstration, not as a production tool. For a more industrial strength implementation of this concept, check out the Composite UI Application Block and other parts of the patterns & practices client guidance.

Here's a test that demonstrates the API of the event broker:

1       [TestMethod]
2       public void ShouldCallSubscriberWhenPublisherFiresEvent()
3       {
4           EventBroker broker = new EventBroker();
5           EventSource1 publisher = new EventSource1();
6           string publishedEventName = "MyEvent";
7           bool subscriberFired = false;
8           EventHandler subscriber = delegate { subscriberFired = true;  };
9
10          broker.RegisterPublisher(publishedEventName, publisher, "Event1");
11          broker.RegisterSubscriber(publishedEventName, subscriber);
12
13          publisher.FireEvent1();
14
15          Assert.IsTrue(subscriberFired);
16      }
17
18  class EventSource1
19  {
20      public event EventHandler Event1;
21
22      public void FireEvent1()
23      {
24          OnEvent1(this, EventArgs.Empty);
25      }
26      protected virtual void OnEvent1(object sender, EventArgs e)
27      {
28          if (Event1 != null)
29          {
30              Event1(sender, e);
31          }
32      }
33
34      public int NumberOfEvent1Delegates
35      {
36          get
37          {
38              if( Event1 == null )
39              {
40                  return 0;
41              }
42              return Event1.GetInvocationList().Length;
43          }
44      }
45  }
46

At line 4, we create the broker. We create an object that exposes a .NET event on line 5. The definition of this type starts on line 18. Notice it has a public event of type EventHandler named Event1 (defined on line 20).

Line 10 is where we register the publisher. The parameters are the name that the broker will use to reference this event (MyEvent), the publishing object, and the name of the event field that actually raises this event (Event1). Note that the name the broker uses and the name the publishing type uses can, and often will, be different.

On line 8, we create an EventHandler delegate instance; this is the subscriber. I'm using the C# anonymous delegate syntax here; in a bigger case this would usually be a reference to an event handling method in a subscribing object, but for the simple case here we don't need another object.

Finally, in line 13 we raise publisher.Event1. This causes the event to fire into the broker, and the broker calls the subscribing delegate. Pretty simple to use.

At least, it's simple in this simple scenario. But going back to our clipboard example, we have three events per publisher, and we'll need to have three subscriptions to match. All those calls to RegisterPublisher and RegisterSubscriber are tedious to write and easily gotten wrong. Wouldn't it be great if we could somehow grab and object and just figure out what needs to be registerd and call it automatically?

Next time, we'll configure ObjectBuilder to do exactly that.

Event broker code download:

EventBrokerSample.zip (24.18 kb)


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Deconstructing ObjectBuilder - Combining Strategies

February 2, 2008 19:09 by Chris

Combining Strategies

We’ve seen how ObjectBuilder can be used to construct objects. But for the simplistic case presented, the extra complexity doesn’t buy us anything. ObjectBuilder doesn’t come into its own until more complex object creation is needed. Multiple strategies can be combined into a single chain to implement arbitrarily sophisticated logic.

For example, it’s a fairly common requirement to cache objects. The first time you request something, you actually create it. Creating these objects can be expensive: reading from databases, calling web services, or whatnot. You don’t want to pay that creation cost every time, so you sock away a copy of the object, and the second and later requests use the already created copy. Let’s look at what it takes to build a simple, general purpose caching factory.

Identifying Objects to Build

Let’s start by writing a test that indicates what we want the API to look like for our new factory. We want to be able to create an object that we don’t already have, but we want to retrieve that exact same object instance the second time. Since we may have multiple Customer objects we wish to retrieve, we should be able to assign and get by an id. Our test looks like this:

1  [TestMethod]
2  public void ShouldGetCachedObjectSecondTime()
3  {
4    CachingFactory factory = new CachingFactory();
5    factory.SetCached<Customer>(true, "Jane");
6
7    Customer c1 = factory.Get<Customer>("Jane", "Jane", "Doe");
8    Customer c2 = factory.Get<Customer>("Jane");
9
10    Assert.AreSame(c1, c2);
11 }

Our factory will have a SetCached method to specify which types should be cached and which shouldn’t be, and the Get method will either create the object we want, or fetch the previously created one.

Note that both methods take a string parameter – this is the ID of the object we want to cache. Notice that the second call to Get doesn’t specify the constructor parameters, just the ID. As long as the ID’s are unique, it doesn’t really matter what the actual value is (it could even be null).

So that’s what we want the API to look like. What do we need to implement it? If you’re thinking strategies and policies, you’re right, but there’s two new pieces that needs to be introduced first.

Build Keys

So far, we've just passed an object's type as the buildKey parameter. Until now, that's all the information we needed, but not anymore. Luckily, build keys can be a lot more than just Type instances.

There's two static method on the BuilderStrategy class, GetTypeFromBuildKey and TryGetTypeFromBuildKey. Let's take a look at the latter:

1  public static bool TryGetTypeFromBuildKey(object buildKey, out Type type) {
2    type = buildKey as Type;
3
4    if(type == null) {
5        ITypeBasedBuildKey typeBasedBuildKey = buildKey as ITypeBasedBuildKey;
6        if(typeBasedBuildKey != null)
7        type = typeBasedBuildKey.Type;
8    }
9
10   return type != null;
11 }

(GetTypeFromBuildKey just calls TryGetTypeFromBuildKey and throws if it fails).

This method starts out with the basic case: if the build key object is a type, it simply returns it. However, if the build key is not a type, it still needs to get a type from it somehow. So it falls back onto the ITypeBasedBuildKey interface, defined as:

1  public interface ITypeBasedBuildKey
2  {
3    Type Type { get; }
4  }

This interface is trivial to implement, but provides a common hook for build keys so that OB can always get a type out.

We need to implement a build key that holds both the type, and the id we're requesting. This is a pretty simple little struct that implements ITypeBasedBuildKey:

1    struct TypeAndIDBuildKey : ITypeBasedBuildKey {
2        public Type TypeToBuild;
3        public string Id;
4
5        public TypeAndIDBuildKey(Type type, string id) {
6            TypeToBuild = type;
7            Id = id;
8        }
9        
10       public Type Type {
11           get { return TypeToBuild; }
12       }
13   }

It's actually important here that the build key is defined as a struct, not a class. As you'll see later, build keys generally have to have value semantics for comparison, which is automatic when using a struct.

Where’s the cache?

Our factory creates objects, but in order to return already existing objects, we’re going to need to hold onto the object references somewhere so we can look them up again later. Thankfully, ObjectBuilder provides a facility to do exactly that.

Let’s look back at the definition of (one of the overloads of) the the IBuilder.BuildUp method:

1  object BuildUp(
2    IReadWriteLocator locator,
3    ILifetimeContainer lifetime,
4    IPolicyList policies,
5    IStrategyChain strategies,
6    object buildKey,
7    object existing);

That first parameter, locator, is what does the trick. Until now, we’ve passed null for this parameter. Now let’s look at what an IReadWriteLocator can do for us. At it’s simplest, a locator is a dictionary. You put objects into it with a given key, and you can later get them back out again using that same key. The IReadWriteLocator interface (and its base interface, IReadableLocator) support a variety of methods to query the locator for its contents.

ObjectBuilder contains an implementation of the IReadWriteLocator interface named, oddly enough, Locator, which implements a weak-referenced dictionary. A weak reference is a reference to an object that does not prevent an object from being collected by the garbage collector. This is actually ideal for our cache; if we’re actively using a cached object, it’ll be available, but if memory pressure gets tight, the GC can clean up those objects that are only referenced by the Locator (i.e. not being used currently).

So, knowing that we need a locator, let’s take a first stab at implementing the CachingFactory:

1   public class CachingFactory {
2       private IReadWriteLocator cache;
3       private StagedStrategyChain<BuilderStage> strategies = 
4           new StagedStrategyChain<BuilderStage>();
5       private PolicyList policies = new PolicyList();
6
7       public CachingFactory() {
8           cache = new Locator();
9       }
10
11      public T Get<T>(string id, params object[] constructorParams) {
12          return (T)(new Builder().BuildUp(
13              cache,
14              null,
15              CreateConstructorParameterPolicy(
16              	typeof(T), id, constructorParams),
17              strategies.MakeStrategyChain(),
18              CreateKey<T>(id),
19              null));
20      }
21
22      public void SetCached<T>(bool shouldCache) {
23          SetCached<T>(shouldCache, null);    
24      }
25
26      private PolicyList 
27      CreateConstructorParameterPolicy(
28          Type typeToCreate, string id,
29          object[] parameters)
30      {
31          PolicyList policies = new PolicyList(this.policies);
32          policies.Set<ICreationParameterPolicy>(
33              new CreationParameterPolicy(parameters),
34              new TypeAndIDBuildKey(typeToCreate, id));
35          return policies;
36      }
37
38      private object CreateKey<T>(string id) {
39          return new TypeAndIDBuildKey(typeof (T), id);
40      }
41  }

We store the locator as a member variable, and pass it in on every call to BuildUp. We use the CreateKey helper method to create our build key, which combines the type and id into a single value that can be passed down to the strategies.

We’ve got the skeleton now, but our test still doesn’t run. In fact, it doesn’t even compile yet. We need to implement the SetCached method. Let’s do that next.

When we needed to pass constructor parameters to the strategies, we used a policy object. Those policies were transient, as we needed to pass different parameters every time. The caching settings, on the other hand, stick around across calls. So we need to build a persistent policy. The difference is trivial; we simply add the new policy to the member variable policy list instead of to the one we create every time.

Defining the policy is pretty simple. We saw in Chapter 1 that the PolicyList itself will map a policy object to a build key. This means our policy itself just needs to indicate if caching is on or off. Our caching policy interface looks like this:

1 	public interface ICachingPolicy : IBuilderPolicy
2 	{
3 	    bool ShouldCache { get; }
4 	}

I wrote two implementations of this interface:

5 	class ShouldCachePolicy : ICachingPolicy
6 	{
7 	    public bool ShouldCache
8 	    {
9 	        get { return true; }
10 	    }
11 	}
12 	
13 	class ShouldNotCachePolicy : ICachingPolicy
14 	{
15 	    public bool ShouldCache
16 	    {
17 	        get { return false; }
18 	    }
19 	}

With these classes in place, we can implement SetCached as follows:

1    public class CachingFactory
2    {
3        private IReadWriteLocator cache;
4        private StagedStrategyChain<BuilderStage> strategies = new StagedStrategyChain<BuilderStage>();
5        private PolicyList policies = new PolicyList();
6
7        private ICachingPolicy shouldCachePolicy = new ShouldCachePolicy();
8        private ICachingPolicy shouldNotCachePolicy = new ShouldNotCachePolicy();
9
10       ...
11
12       public void SetCached<T>(bool shouldCache, string id) {
13          ICachingPolicy cachingPolicy = shouldNotCachePolicy;
14          if(shouldCache) {
15              cachingPolicy = shouldCachePolicy;
16          }
17          policies.Set<ICachingPolicy>(cachingPolicy, CreateKey<T>(id));
18      }
19      
20      ...
21  }

The important line here is the call to Policies.Set on line 17. This sets the policy into the builder’s persistent policy list. This is automatically passed to the strategy chain on the call to BuildUp. We use the build key (which includes the type and id) to set the policy, so they can be looked up later.

Strategies, and the combination thereof

So now the builder can tell us if an object should be cached or not. Next, we need to add the strategies that actually implement the cache. The construction logic goes something like this:

  • If object should be cached:
    • If object is present in the locator, return it
    • Else:
      • Create the object
      • Store it in the locator
    • Return created object

We already implemented the “Create the object” step in Chapter 1, and I’d like to reuse that work. Let’s look at what’s required to do the look up and storage steps. We’ll implement these two steps as separate strategies. This makes sense, as the need to happen at different times in the pipeline. Looking the cached object up happens first, so let’s start there.

Our CacheRetrievalStrategy looks like this:

1   class CacheRetrievalStrategy : BuilderStrategy {
2       public override object BuildUp(
3       	IBuilderContext context, 
4       	object buildKey, 
5       	object existing)
6       {
7           ICachingPolicy cachePolicy = 
8               context.Policies.Get<ICachingPolicy>(buildKey);
9               
10          if(cachePolicy != null ) {
11              if(cachePolicy.ShouldCache) {
12                  object cached = context.Locator.Get(buildKey);
13                  if(cached != null) {
14                     return cached;
15                  }
16              }
17          }
18          return base.BuildUp(context, buildKey, existing);
19      }
20  }

Let’s walk though the implementation.

Lines 7-8 retrieve the cache policy for the currently requested build key. Not having a caching policy (if context.Policies.Get returns null) is the same as saying “don’t cache”. If we do have a caching policy, and the policy says to cache (lines 10-11) we need to look up the object in the locator.

Lines 12 uses the build key to look up the object in the current locator (as provided in the build context). By the way, this is why build keys should have value semantics: they're used as lookup keys for both policies and in the locator. If they compare by reference, later lookups will probably fail, as individual build key objects get recreated regularly.

If we find an object in the locator, we return it immediately (line 14). This short-circuits the rest of the strategy chain, which makes sense as the object is already created.

If the object is not found in the locator, then it needs to be created. Rather than do the work here, this strategy simply lets the strategy chain continue via a call to base.BuildUp (line 18).

Now that we can look stuff up in the locator, let’s look at the flip side, which is storing the created object in the locator. The implementation is equally straightforward:

1   class CacheStorageStrategy : BuilderStrategy {
2       public override object BuildUp(
3           IBuilderContext context,
4           object buildKey, 
5           object existing)
6       {
7           ICachingPolicy cachePolicy = 
8               context.Policies.Get<ICachingPolicy>(buildKey);
9           if(cachePolicy != null) {
10              if(cachePolicy.ShouldCache) {
11                  context.Locator.Add(buildKey, existing);
12              }
13          }
14          return base.BuildUp(context, buildKey, existing);
15      }
16  }

The overall skeleton of the code is identical to the CacheRetrievalStrategy – the caching policy is retrieved in the exact same way (lines 7-8). The big difference is on line 11. Here, instead of getting a value from the locator, we’re adding it. The object that’s being constructed (and therefore needs to be cached) is being passed in via the “existing” parameter. So we go ahead and put it in the locator if current policy settings say we should.

Finally, we call base.BuildUp again, so that if there are any strategies after this one they get a fair shot at the object.

We now have our strategies, so we need to add them to the builder. We can take advantage of the builder stages to make sure that the strategies are in the correct order. We’ll put the cache retrieval in the pre-creation stage, the creation strategy in the creation stage as before, and we’ll put the cache storage in the post-initialization stage. Our builder’s constructor looks like this:

1 	public CachingFactory()
2 	{
3 	  strategies.AddNew<CacheRetrievalStrategy>(BuilderStage.PreCreation);
4 	  strategies.AddNew<BasicCreationStrategy>(BuilderStage.Creation);
5 	  strategies.AddNew<CacheStorageStrategy>(BuilderStage.PostInitialization);
6 	  cache = new Locator();
7 	}

And with this, finally, that original test passes.

Where are we?

We’ve seen how to combine multiple strategies to implement more complex creation logic. We’ve also seen several of the options that ObjectBuilder provides for communication across strategies. These include:

  • Persistent policy objects so that the builder can configure how the strategies work.
  • A locator object to store objects across calls to BuildUp. Objects in the locator are typically indexed via build key (but can use any arbitrary object as long as it has compare-by-value semantics).
  • Passing the constructed object down the chain via the “existing” parameter so that later stages can work with or on the constructed object. We also make our first use of the build key to identity the objects we were creating, and look them up later.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

What version of ObjectBuilder are we talking about here?

January 31, 2008 17:11 by Chris

I got a comment on the first installment of Deconstructing ObjectBuilder that asked an excellent question: what version of OB is it talking about?

The current production version (which I'll call OB1) is used in CAB, Entlib 2 and 3, and WCSF. The document is written against ObjectBiulder 2. This version was written by Scott Densmore and Brad Wilson, and uploaded to the ObjectBuilder Codeplex project as a sample. We (p&p) have taken this to be the underlying engine for the Unity container, primarily because it fixes some significant underlying issues with OB1.

So, if you're wonding why my code doesn't compile, that's why. You'll need to grab OB2 off of codeplex. It's checked into their source tree. We will be releasing a new (slightly tweaked) version of this codebase with the Unity container, which hopefully should have a CTP soon.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Deconstructing ObjectBuilder - What Is ObjectBuilder?

January 25, 2008 17:12 by Chris

Dependency Injection and confusion

ObjectBuilder is most often described as a Dependency Injection (DI)  tool. But looking at the code doesn't really reveal much in the way of how to use this thing to do dependency injection. Instead, you get almost immediately swamped in locators, strategies, builders, and what's that build key supposed to be anyway?

have also heard ObjectBuilder described as "A framework to build dependency injection containers." This is both inaccurate and scary; we have to build something on top of this thing to get the promised dependency injection goodness? If ObjectBuilder is all about DI, why doesn't it actually do it?

The shipping parts in ObjectBuilder do actually implement a reasonable version of DI once you figure out how to assemble everything. Figuring out how to assemble the parts unfortunately requires reading the OB code. And if you read the code with DI in mind, you'll find yourself lost very quickly (I sure did).

he reason for this confusion comes from a basic misunderstanding of what OB actually does. OB is not a DI container. It's also not a DI container framework. ObjectBuilder is actually a configurable object factory.

Object Factories

So what does that mean? The concept of an object factory is fairly straightforward: it's something that you ask for an instance of some type, and it gives you one. The Gang of Four's Design Patterns book includes three different patterns that involve a factory object: Abstract Factory defines an entire class to create instances of a set of objects, Builder encapsulates the details of constructing a set of objects, and Factory Method defines a method you call to get your objects.

At first glance, the factory is an underappreciated concept. Why not just call new instead? The fundamental reason is coupling. When you call new, you hard code the concrete type you'll be creating. For example: 

    public decimal CalculateTaxes(Citizen citizen)
    {
        FederalTaxCalculator taxCalculator = new FederalTaxCalculator();
        return taxCalculator.CalculateTaxFor(citizen);
    }

This code is just fine, except - what happens when marketing decides that we should also support taxes for states as well? Do we do this:

    public decimal CalculateTaxes(Citizen citizen)
    {
        WashingtonAndFederalTaxCalculator taxCalculator = new WashingtonAndFederalTaxCalculator();
        return taxCalculator.CalculateTaxFor(citizen);
    }

That's great for Washington customers, but now we need to maintain 50 separate versions of the software, one for each state. This is typically where we define an interface to define the commonality between the calculator objects.

    public interface ITaxCalculator
    {
        decimal CalculateTaxFor(Citizen citizen);
    }

This now lets us write our CalculateTaxes methods in terms of the interface:

    public decimal CalculateTaxes(Citizen citizen)
    {
        ITaxCalculator calculator = new FederalTaxCalculator();
        return calculator.CalculateTaxFor(citizen);
    }

Unfortunately, this interface didn't actually buy us anything. The problem is the call to new. We've hard coded the concrete type in there, which means we're stuck with creating separate versions. This is the point where a factory gets introduced. For example, using an Abstract Factory object:

    public decimal CalculateTaxes(Citizen citizen)
    {
        TaxCalculatorFactory factory = new TaxCalculatorFactory();
        ITaxCalculator calculator = factory.CreateCalculator();
        return calculator.CalculateTaxFor(citizen);
    }

The TaxCalculatorFactory object encapsulates all the details of figuring out which tax calculator object to create. It could use any method desired to figure out what concrete class to create, but our client code neither knows nor cares about the details.

In modern systems, you'll often find yourself writing small, custom factory classes like this. This is exactly what happened in p&p during the development of Enterprise Library and CAB. The repetition of writing this stuff resulted in ObjectBuilder.

ObjectBuilder - The Generic Factory

ObjectBuilder is a library that lets you set up object factories in a highly configurable way. Using OB uses several classes together:
  • Builder: A Builder object is the actual factory object you call into, but it doesn't actually do the creation work, but it doesn't actually create the objects. Instead, it sets up the rest of the factory, and calls into a Strategy Chain.
  • Strategy Chain: An ordered collection of strategy objects. The Strategy Chain makes sure that the strategies run in the correct order.
  • Strategy: A class that performs one part of the construction process.
  • Policy: Policy objects are used by strategies to communicate down the chain with other strategies, and to provide customization of the strategy's actions.
That was pretty abstract, so let's build a simple example.

A Roundabout Way to Create Objects

Let's start by assembling a simple factory that'll create objects of a requested type for us.

The Entry Point

The entry point to an OB based factory is the IBuilder interface:

    public interface IBuilder
    {
        object BuildUp(IReadWriteLocator locator,
            ILifetimeContainer lifetime,
            IPolicyList policies,
            IStrategyChain strategies,
            object buildKey,
            object existing);

        TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator,
            ILifetimeContainer lifetime,
            IPolicyList policies,
            IStrategyChain strategies,
            object buildKey,
            object existing);

        TItem TearDown<TItem>(IReadWriteLocator locator,
            ILifetimeContainer lifetime,
            IPolicyList policies,
            IStrategyChain strategies,
            TItem item);
}


The method of interest here is the BuildUp method. This is the one that you call to actually initiate the construction of an object. A builder has a list of Policy objects and a list of Strategy object that it uses to actually perform the creation of the object. There are a lot of details in this small interface; we'll hit them all as we go through the tutorial. For now, please allow me to gloss over the specifics for the moment. We'll get back to them, I promise.


There is an implementation of this interface called, imaginatively enough, Builder, that ships with the ObjectBuilder library. This class provides a straightforward implementation of the interface, but there are a lot of separate things you have to manage to call BuildUp. So, we'll create our own factory class that wraps the underlying Builder object and manages these extra parts.

Let's create a simple test that demonstrates how we want our new factory to behave. The simplest use is to create an object without any parameters:

        [TestMethod]
        public void ShouldCreateObjectGivenRuntimeType()
        {
            BasicFactory factory = new BasicFactory();
            object result = factory.Create(typeof (Customer));
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(Customer));
        }

    class Customer
    {
        private string firstName;
        private string lastName;


        public Customer()
        {
        }

        public Customer(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }
    }

Our BasicFactory class will have one method, Create, that takes a Type object indicating which type to create. So, let's implement IBuilder.

Luckily, there's a base class already in the library, BuilderBase, which takes care of the vast majority of the details when implementing IBuilder. So we'll inherit from that. Our first cut at the BasicFactory looks like this:

public class BasicFactory : BuilderBase<BuilderStage>
{
    public object Create(Type t)
    {
        return null;
    }
}

This is just to get the test to compile, but obviously it doesn't pass. Let's implement that Create method now.

Invoking the Builder

Remember that BuildUp method above? This is the "real" method that actually kicks off the ObjectBuilder process. So, all we need to do (all? Hah!) is create a Builder instance and call BuildUp. Unfortunately, BuildUp has several parameters. We can safely pass null for the IReadWriteLocator and ILifetimeContainer, so we'll do that for now (I'll talk about what these objects do in a future installment). But one thing we really need is a StrategyChain, with a strategy in it. So let's build one.

Creating the BasicCreationStrategy

Strategy objects do the actual work in ObjectBuilder. The Builder class simply invokes a set of strategies. Strategies implement the IBuilderStrategy interface:

    public interface IBuilderStrategy
    {
        object BuildUp(IBuilderContext context,
                       object buildKey,
                       object existing);

        object TearDown(IBuilderContext context,
                        object item);
    }

Only two methods, so not too complicated. BuildUp is called when the strategy is invoked during a call to IBuilder.BuildUp. TearDown is called when ObjectBuilder is being used to manage object lifetime, and the object is going away (more about this in later installments).


The parameters to BuildUp are:

  • IBuilderContext: This contains information about the current build operation. The context allows access to the current policy set, and the current set of strategies being executed.
  • object buildKey: The build key object provides information about the type that's currently being built. It can just be a Type object (which is what we'll use here), but can be another object containing additional information if you need it. (More about this later).
  • object existing: A call to BuildUp might pass in an object that's already been created, or an earlier strategy might have created and object and then passed it down the chain for later work.

The ObjectBuilder code includes an implementation of IBuilderStrategy called, amazingly enough, BuilderStrategy. This base class is, in practice, used to implement strategy objects rather than implementing the interface directly. 

Our BasicCreationStrategy looks like this:

public class BasicCreationStrategy : BuilderStrategy
{
    public override object BuildUp(IBuilderContext context, object buildKey, object existing)
    {
        object result = existing;
        if(result == null)
        {
            Type typeToBuild = BuilderStrategy.GetTypeFromBuildKey(buildKey);
            result = Activator.CreateInstance(typeToBuild);
        }
        return base.BuildUp(context, buildKey, result);
    }
}

Fundamentally, this is just a call to Activator.CreateInstance. However, it's slightly more complicated because I want a well behaved strategy class.  Strategies are invoked in a chain. We want to make sure we don't destroy any work done by a previous strategy, so we make sure that we don't create a new instance if we already have one. And afterward, we want to pass our created instance to the next strategy in the chain, so we call the base class's implemention of BuildUp, which invokes the rest of the strategy chain for us.

Inserting the Strategy and Builder Stages

So now I have my strategy. I need to insert it into a strategy chain. Since I always want BasicFactory to use this strategy, it's easiest to simply add it in a constructor:

public class BasicFactory
{
    StagedStrategyChain<BuilderStage> strategies = new StagedStrategyChain<BuilderStage>();
    public BasicFactory()
    {
        strategies.AddNew<BasicCreationStrategy>(BuilderStage.Creation);
    }
...

The call to strategies.AddNew creates a new instance of our BasicCreationStrategy class. The parameter, BuilderStage.Creation, is kind of interesting here. Did you notice in the definition of the strategies variable above, that the StagedStrategyChain has a generic type parameter? This type parameter defines the builder Stages, and is typically an enum (it could be an int if you want). Each strategy is in one and only one stage. The strategies are executed in order that the stages are defined. OB ships with the BuilderStage enum, which makes an excellent default. It defines the following stages: PreCreation, Creation, Initialization, and PostInitialization. All strategies placed in the PreCreation stage execute first, followed by the strategies in the Creation stage, and so on. Within a stage, the strategies execute in the order they were added. Stages aren't strictly necessary, but they do provide users of a builder the option to add extra strategies at various points in the chain.

Let's put the entire class together and implement the Create method:

public class BasicFactory
{
    private StagedStrategyChain<BuilderStage> strategies = new StagedStrategyChain<BuilderStage>();
    private PolicyList policies = new PolicyList();

    public BasicFactory()
    {
        strategies.AddNew<BasicCreationStrategy>(BuilderStage.Creation);
    }

    public object Create(Type t)
    {
        Builder builder = new Builder();
        return builder.BuildUp(
            null,
            null,
            policies,
            strategies.MakeStrategyChain(),
            null);
    }
}

In our case, we only have the one strategy, and the Creation stage seems to be the appropriate place to put it. If we now run our test, it passes!

Using Policies to Pass Information

So, we now have a factory that can create an object of any type we pass to it. Except - what about object that take constructor parameters? My little Customer object has a constructor that lets you set the first and last name. Let's write another test that shows that we can pass parameters to the Create method:

        [TestMethod]
        public void ShouldCreateObjectWithParameters()
        {
            BasicFactory factory = new BasicFactory();
            object result = factory.Create(typeof (Customer), "John", "Doe");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof (Customer));
            Customer c = (Customer) result;
            Assert.AreEqual("John", c.FirstName);
            Assert.AreEqual("Doe", c.LastName);
        }
Getting this to compile requires a slight tweak to the definition of the Create method. Specifically, passing in the arguments:

    public object Create(Type t, params object[] constructorArgs)
    {
        Builder builder = new Builder();
        return builder.BuildUp( null, null,
            policies,
            strategies.MakeStrategyChain,
            t,
            null);
    }

Now we have a bit of a problem. Although we've added the constructor arguments to our Create method, looking at the signature of BuildUp, there's nowhere to put them. So how do we get the parameters into the call, so that the strategy can take advantage of them?

If we look at the definition of BuildUp, the first parameter is a locator object. This is one possibility: the locator lets strategies look up objects via an arbitrary key. However, locators in general are intended to be used to store information that lasts longer than a single call to BuildUp. Since the parameters are only used in this one call, the locator isn't really appropriate.

So, we need to put the parameters somewhere else. That somewhere else is a Policy object.

Policies provide an "out of band" mechanism for strategies to get extra information. In our case, we need to store away the constructor parameters someplace that the strategy can find later.

 
It's generally a good idea to provide an interface that defines the contract of your policy, so that you can provide multiple implementations later. So, I started with this interface:

public interface ICreationParameterPolicy : IBuilderPolicy
{
    object[] Parameters { get; }
}

Policy interfaces derive from IBuilderPolicy. IBuilderPolicy doesn't actually have any methods; it's used as a marker interface to tell the OB plumbing that this is, indeed a policy object.

So we have an interface that lets us retrieve the constructor parameters. Let's implement it:

public class CreationParameterPolicy : ICreationParameterPolicy
{
    private object[] parameters;

    public CreationParameterPolicy(params object[] parameters)
    {
        this.parameters = parameters;
    }

    public object[] Parameters
    {
        get { return parameters; }
    }
}

We have a fairly trivial data-holder object here. Next up, we need to use this policy in our strategy object. Policies are looked up via a method on the builder context object passed to BuildUp. The new implementation of our strategy is:

    public override object BuildUp(IBuilderContext context, object buildKey, object existing)
    {
        object result = existing;
        if(result == null) {
            Type typeToBuild = BuilderStrategy.GetTypeFromBuildKey(buildKey);
            ICreationParameterPolicy policy =
                context.Policies.Get<ICreationParameterPolicy>(buildKey);
            if (policy != null) {
                result = Activator.CreateInstance(typeToBuild, policy.Parameters);
            } else {
                result = Activator.CreateInstance(typeToBuild);
            }
        }
        return base.BuildUp(context, typeToBuild, result, idToBuild);
    }

The lookup of the policy occurs at the call to context.Policies.Get. It takes one generic type parameter, the type of policy to retrieve, and a key to look up the specific policy. As seen here, typically you'll use the build key as the parameter to look up your policy.

If we have the policy object in our context, we then pass the results of calling policy.Parameters to Activator.CreateInstance, and voila! We have construction with parameters. Unfortunately, if you run our test and debug through, we don't actually have the policy object. So how do you get the policy object into the context?

If we go back and look at the signature of IBuilder.BuildUp:

        object BuildUp(IReadWriteLocator locator,
            ILifetimeContainer lifetime,
            IPolicyList policies,
            IStrategyChain strategies,
            object buildKey,
            object existing);

Take a look at that third parameter. The policy list is passed into the BuildUp call. We already have a policy list as a member variable of our factory class, so we could just stick it in there. But that's not quite what we want. Remember, this particular policy should only be used for the current BuildUp call. If we put it in the member variable, it'll stick aroudn across calls (unless we explicitly remove it). Luckily, the PolicyList class supports a hierarchy. When you create a PolicyList, you can specify a parent. If a lookup in the current PolicyList fails, it'll try its parent. This gives us the ability to have persistent and transient policies. Persistent policies are added to the member variable in the factory itself (much like strategies are), and are available every time the builder object is used. Transient policies, on the other hand, are only available for the lifetime of a single call to BuildUp. In this case, we only want the constructor parameters to be available for the construction of this single object, since they'll change on every call to the builder. So, we'll add a transient policy. The new implementation of BasicFactory.Create looks like this:

    public object Create(Type t, params object[] constructorArgs)
    {
        Builder builder = new Builder();
        PolicyList transientPolicies = new PolicyList(policies);
        transientPolicies.Set<ICreationParameterPolicy>(new CreationParameterPolicy(constructorArgs), t);
        return builder.BuildUp( null, null,
            transientPolicies,
            strategies.MakeStrategyChain(),
            t,
            null);
    }

We create a new PolicyList object, and then we add a new CreationParameterPolicy to it. Notice the Set call takes one type parameter and two regular parameters: the type of the policy, the policy object instance, and key to use (the type of object we're creating in this case). This corresponds to the Get call in our strategy above. Finally, we pass this new PolicyList to base.BuildUp, and our policy is now available to our strategy. The test passes.

Where Are We?

So far, we've written three classes and an interface to reproduce the effect of a single-line call to Activator.CreateInstance. Using ObjectBuilder doesn't really make sense for this particular usage, but along the way we've hit many of the "moving parts" of the ObjectBuilder framework that will serve us well later:

  • A Builder object maintains a chain of Strategy objects, and invokes them in order when requested.
  • Strategies provide the actual logic used in the creation process.
  • Policies provide additional "out of band" logic, and are looked up based on a policy type and key.

Next time, let's look at how we can combine strategies to provide more than just a big wrapper around a call to new.


Currently rated 5.0 by 7 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5