My first attempt was to use spring.net mix-ins, or introductions in AOP terms. That kinda worked, but since mixing require a class with the interface implementation, I still needed to write the decorator... not a whole lot better.
After more googling, turns out what I wanted is called duck-typing (who knew). And as with other problems, once you get the term, the googling is easy. I found a few different libraries that could help, I settled on impromptu.
It did take me a while to work out a good way of integrating with spring.net, i've used spring.net a fair bit, but haven't done a lot of extension work to date. Finally I settled on a custom factory object, it requires the target objec rand target interface, using that, it produces a ready to go decorator.
/emote "<-- nods and files away a new tool for the toolbox"
A quick sample class:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using ImpromptuInterface; | |
using Spring.Objects.Factory.Config; | |
namespace derek.kowald.BaseWebApp.Core.Extensions.Spring | |
{ | |
/// <summary> | |
/// Custom <a href='http://www.springframework.net/'>spring.net</a> | |
/// factory for creating an object that implements | |
/// a specified interface, using a target object (<a href='http://en.wikipedia.org/wiki/Duck_typing'>duck-typing</a>). | |
/// </summary> | |
/// <remarks> | |
/// Leverages Impromptu for the heavy lifting, see | |
/// <a href="http://code.google.com/p/impromptu-interface/">http://code.google.com/p/impromptu-interface/</a> | |
/// Usefull for putting an interface onto a non-interface class, as found in many | |
/// .NET framework classes. | |
/// </remarks> | |
/// <example> | |
/// var raw = new MyObject(); | |
/// var factory = new SpringDuck() | |
/// { | |
/// TargetInterface = typeof(IOther), | |
/// TargetObject = raw | |
/// }; | |
/// factory.AfterPropertiesSet(); | |
/// IOther wrapped = (IOther)factory.GetObject(); | |
/// </example> | |
public class SpringDuck : AbstractFactoryObject | |
{ | |
#region Properties | |
/// <summary> | |
/// Gets or sets the target <see cref="System.Type"/> interface. | |
/// </summary> | |
public Type TargetInterface { get; set; } | |
/// <summary> | |
/// Gets or sets the target object instance to delegate interface calls to. | |
/// </summary> | |
public object TargetObject { get; set; } | |
/// <summary> | |
/// Gets the factory returns a 'type'. This is defined by <see cref="TargetInterface"/>. | |
/// </summary> | |
public override Type ObjectType | |
{ | |
get { return TargetInterface; } | |
} | |
#endregion | |
#region Impl | |
/// <summary> | |
/// Post set-up checks the factory is configured. | |
/// </summary> | |
public override void AfterPropertiesSet() | |
{ | |
if (TargetInterface == null) | |
{ | |
throw new ArgumentException("The TargetType must be provided.", "TargetType"); | |
} | |
if (TargetObject == null) | |
{ | |
throw new ArgumentException("The TargetObject msut be provided.", "TargetObject"); | |
} | |
if (!TargetInterface.IsInterface) | |
{ | |
throw new ArgumentException("The TargetType must be an interface.", "TargetType"); | |
} | |
base.AfterPropertiesSet(); | |
} | |
/// <summary> | |
/// Creates a dynamic object with the <see cref="TargetInterface"/>, using | |
/// <see cref="TargetObject"/> to implement the interface. | |
/// </summary> | |
protected override object CreateInstance() | |
{ | |
return Impromptu.DynamicActLike(TargetObject, TargetInterface); | |
} | |
#endregion | |
} | |
} |
No comments:
Post a Comment