This code uses an Action<T> underneath to create a new instance of the type that is going to be sent or published. What is extra cool about this is that NSB allows you to define values for properties of an instance that it will dynamically generate. Like the example above, this allows NSB to give the impression that it can assign values directory to interface properties!
So, how can we allow users of our API to define properties, but keep hold of the specifics about how we create an object?
Action<T> as Builder
Like NSB, we can take in an Action<T> and allow the user of the API to build the object they desire. To show the specifics of how to do this, I'll complete a non-production version of what NSB does. So, first we need to define the IBus interface:
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
public interface IBus | |
{ | |
void Publish<T>(Action<T> action); | |
} |
And this is a simple implementation for IBus:
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
public class Bus : IBus | |
{ | |
// this is our dependency that will publish the message to a queue (or somewhere else) | |
private IMessageDispatcher messageDispatcher; | |
public void Publish<T>(Action<T> action) where T : IMessage | |
{ | |
var newInstance = CreateInstance<T>(); | |
// pass the new instance to the action (by reference) | |
action(newInstance); | |
messageDispatcher.Publish(newInstance); | |
} | |
private T CreateInstance<T>() | |
{ | |
var result = default(T); | |
// do something clever creating an initialized T instance | |
return result; | |
} | |
} |
A bit of explanation
So, it turns out that the code is quite simple for a pretty cool API feature. The implementation of IBus only needs to understand the type of the message, then can create a blank instance, and pass the instance to the action for building. At this point, the action implementation (defined in the method call to IBus.Publish<T>) takes over and fills in all the properties. This works because T has to be an IMessage (which means it must be a reference type). Nice. Using this type of technique would also allow the framework code to make a default instance (with some properties set) and let these values to be changed during the action implementation.
More to come...
No comments:
Post a Comment