So this will be my 8:th post about
ObjectBuilder and probably the last one for a while since I think I covered quite a lot by now. In this post I will use an example from
CAB, but with some small modifications to decouple it from CAB and make it useful without needing CAB.
CAB has great support for the
Command design pattern, and the support is built around ObjectBuilder. They have a CommandStrategy that looks for the CommandHandler attribute on public methods and if found, it will hook the Command objects ExecuteAction event to that method. One of the things this allows you to do is to handle your commands in the Presenter (when using MVP) in a really easy way. But lets focus on the strategy class since that is the focus of this post.
1 public class CommandStrategy : BuilderStrategy
2 {
3 ///<summary>
4 /// Inspects the object for command handler declarations registering them with the
5 /// corresponding <see cref="Command"/>.
6 ///</summary>
7 public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
8 {
20 }
21
22 ///<summary>
23 /// Inspects the object for command handler declarations unregistering them from the
24 /// corresponding command.
25 ///</summary>
26 public override object TearDown(IBuilderContext context, object item)
27 {
39 }
40
88 }
To create your own strategy you start by inheriting the abstract BuilderStrategy class in ObjectBuilder. For your strategy to do anything useful you should override at least the BuildUp method (line 7) and probably also TearDown (line 26). The name of the methods should say it all, but just in case, BuildUp is called when the object is created and TearDown when the object should be freed.
Now lets continue with the example of hooking up Command.ExecuteAction event.
9 ICommandContainer commandContainer = GetCommandContainer(context, existing);
10
11 if (commandContainer != null && existing != null)
12 {
13 Type targetType = existing.GetType();
14
15 foreach (MethodInfo methodInfo in targetType.GetMethods())
16 RegisterCommandHandlers(context, commandContainer, existing, idToBuild, methodInfo);
17 }
18
19 return base.BuildUp(context, typeToBuild, existing, idToBuild);
The first thing I do in my BuildUp method is trying to get a reference to an ICommandContainer interface. This is an interface that I have defined with one property that should expose a collection of Command objects. This is where CAB asks for a WorkItem instead and therefore is very tightly coupled to CAB. In the GetCommandContainer, I simply use the locator object to ask for the interface. The next interesting thing in the code is the loop on line 15 and 16 that uses reflection to inspect every public method in the class for the CommandHandler attribute. The actual inspection is happening in the RegisterCommandHandlers method, shown below.
40 private void RegisterCommandHandlers(IBuilderContext context, ICommandContainer commandContainer, object target,
41 string targetID, MethodInfo methodInfo)
42 {
43 foreach (CommandHandlerAttribute attr in methodInfo.GetCustomAttributes(typeof(CommandHandlerAttribute), true))
44 {
45 Command cmd = commandContainer.Commands[attr.CommandName];
46
47 if (!cmd.IsHandlerRegistered(target, methodInfo))
48 cmd.ExecuteAction += CreateCommandHandler(target, methodInfo);
49 }
50 }
If the method has the CommandHandler attribute the code gets a reference to the command, first checks that the event is not already hooked for this method, and otherwise it hooks the event.
62 private static EventHandler CreateCommandHandler(object target, MethodInfo methodInfo)
63 {
64 Delegate handler = null;
65
66 if (methodInfo.IsStatic == false)
67 {
68 handler = Delegate.CreateDelegate(typeof(EventHandler), target, (MethodInfo)methodInfo);
69 }
70 else
71 {
72 throw new InvalidOperationException(
73 string.Format(CultureInfo.CurrentCulture,
74 Resources.Resource.StaticCommandHandlerNotSupported, methodInfo.Name));
75 }
76
77 return (EventHandler)handler;
78 }
The TearDown method just does the reverse, but since this is already a very code intensive post I left that part of the code, but you can easily find the complete class and all other classes needed for the pattern in CAB (with the exception of my own interface, mentioned above).
When you have created your own strategy all you have left to do is to add it to the strategies collection of the builder.