Trick or Trait? Composite Oriented Programming with C#
Last year, Rickard Öberg unveiled his Qi4j project. Qi4j is a framework for domain centric development thru Composite Oriented Programming (COP). Composition of larger objects from finer grained fragments is one of the core principles of this paradigm, and Qi4j does it by piecing together mixins to form instances.
Some of you might be familiar with traits which is a very similar concept. Traits are a simple composition mechanism for structuring object oriented programs. Essentially, a trait is a behavioral building block which is the primitive unit of code reuse.
Traits have been implemented in a number of programming languages, and there is even a Microsoft funded research project which explores traits as a C# feature.
Since the 3rd pre-release of Qi4j hit the streets earlier this month, the buzz around the framework has increased, and I decided to blow the dust of a small prototype I started thinking about when I last met Rickard for a couple of beers and a lot of geek-speak.
I must admit that I find the idea of composing large scale behavior from tiny, specialized bits and pieces, especially because the way sound design principles force their way into ones code.
Qi4j is a full fledged framework built on the patterns described in Eric Evans’ seminal Domain Driven Design book, and if you look at demos on their site you’ll notice that many of the mechanisms in the book manifest themselves clearly in the code. My humble demo does not feature all of the Qi4j concepts, but it does show how one can do instance composition in C# with a little help from Castle Windsor.
Before digging in to the nitty-gritty details of how it actually is done, lets take a look at how we can compose some “traits” into a composite.
Since the “traits” are behavioral in nature, we begin by describing them as interfaces.
public interface IDialogSettings
{
string Name { get; set; }
string Phrase { get; set; }
}
public interface ISpeaker
{
string Say();
}
Those who are familiar with Qi4j probably recognize these from the Hello World demo. The IDialogSettings describes the state part used by the composites, while the ISpeaker interface describe the ability to “say something”. The implementation for IDialogSettings is straight forward, so we can safely skip that and look at the ISpeaker implementation (which is kind of straight forward as well).
public class Speaker : ISpeaker
{
private IDialogSettings settings;
[This]
public IDialogSettings Settings
{
get { return settings; }
set { settings = value; }
}
public string Say()
{
return string.Format("{0} says \"{1}\".",settings.Name,settings.Phrase);
}
}
Notice that the implementation has a dependency on IDialogSettings that is not part of the interface and that this property is marked with the mysterious This attribute. This is a marker which is used to bind to other “traits” on the same composite. More on this when we look behind the scenes.
Finally we describe the composite with yet another interface;
public interface IWorldGreeter : ISpeaker, IDialogSettings {}
Composition happens at runtime, so we’ll need to register the “traits”, the composite and the Qi# facility in the container.
<castle>
<facilities>
<facility id="qisharp.facility"
type="Noras.Sandbox.QiSharp.CompositeFacility" />
</facilities>
<components>
<component id=”settings”
service=”Noras.Sandbox.QiSharp.IDialogSettings”
type=”Noras.Sandbox.QiSharp.DialogSettings”
lifestyle=”Transient” />
<component id=”speaker”
service=”Noras.Sandbox.QiSharp.ISpeaker”
type=”Noras.Sandbox.QiSharp.Speaker”
lifestyle=”Transient” />
<component id=”greeter”
service=”Noras.Sandbox.QiSharp.IWorldGreeter”
isComposite=”true” />
</components>
</castle>
We can resolve a composite instance just like any other type;
IWorldGreeter helloWorld = container.Resolve<IWorldGreeter>();
helloWorld.Name = "Marvin";
helloWorld.Phrase = "Hello World!";
Assert.AreEqual("Marvin says \"Hello World!\".", helloWorld.Say());
As you might suspect, the instance is a proxy. Lets take a look at the component activator responsible for creating composite components.
protected override object InternalCreate(CreationContext context)
{
ProxyGenerator gen = new ProxyGenerator();
Type compositeInterface = context.Handler.ComponentModel.Service;
Dictionary<Type, object> traits = new Dictionary<Type, object>();
foreach (Type traitInterface in compositeInterface.GetInterfaces())
{
object traitImpl = Kernel.Resolve(traitInterface);
if (traitImpl != null)
{
traits.Add(traitInterface, traitImpl);
}
}
// Wire internal dependencies...
foreach (object trait in traits.Values)
{
Type traitImplType = trait.GetType();
foreach (PropertyInfo property in traitImplType.GetProperties())
{
if (Attribute.IsDefined(property,typeof(ThisAttribute)))
{
if (traits.ContainsKey(property.PropertyType))
{
property.SetValue(trait,traits[property.PropertyType],null);
}
}
}
}
object proxy = gen.CreateInterfaceProxyWithoutTarget(
compositeInterface,
new CompositeInvocationInterceptor(traits));
return proxy;
}
First we create a dictionary of “trait” instances by resolving all of the implemented interfaces from the container, then we scan these for This attributes and wire the dependencies whenever we find one, regular dependencies are resolved in a regular manner.
Since constructor can’t be defined on interfaces, constructor injection is not an option within the composites - hence we fallback to property injection here.
The “traits” dictionary is then passed to the interceptor instance which will be responsible for routing the method invocations on the composite to the “traits” supporting the behaviors.
public void Intercept(IInvocation invocation)
{
Type targetTrait = invocation.MethodInvocationTarget.DeclaringType;
if (traits.ContainsKey(targetTrait))
{
invocation.ReturnValue = invocation.Method.Invoke(
traits[targetTrait],
invocation.Arguments);
}
}
We discover which “trait” the method was invoked on by finding the type the method is declared on, then we get that “trait” from the dictionary created in the component activator. All in all it’s quite simple.
To give you a taste of how powerful composite oriented programming can be, let’s throw a little “trait” reuse into the mix.
ITeleprompter teleprompter = container.Resolve<ITeleprompter>();
teleprompter.Phrase = "Hello World";
Assert.AreEqual("Say Hello World.",teleprompter.WhatToSay);
IWorldGreeter helloWorld = container.Resolve<IWorldGreeter>();
helloWorld.Name = "Marvin";
helloWorld.Phrase = "Hello World!";
Assert.AreEqual("Marvin says \"Hello World!\".", helloWorld.Say());
In this example, the ITeleprompter and the IWorldGreeter composites share the same implementation of the IDialogSettings “trait”. To put it another way, the “trait” is reused between the two composites.
Since we’re using a container here, we could also change the lifestyle of the IDialogSettings component from transient to singleton, in which case the two composites would share state.
I only spent an hour on this example, so I haven’t taken the time to implement all of the other things Qi4j sport. If you’re curious, there is quite a lot of information available at the Qi4j website.
I’ll post the code as soon as I’ve found time to clean it up.