This a continuation from here, here and here.
So far, the object builder can create instances using either constructors or factory methods. It can also access custom configuration sections. Now I am going to show how we can extend it to provide type substitution. Please note that by "type substitution" I do literally mean subsitituting one type for another. An example would be that a consumer asks for an interface IDog and gets an instance of a class that implements IDog. There are many reasons why we would do this:
-
Complete seperation of concerns (e.g. views and business objects).
-
Creating plugins for an application.
-
Changing the behavior of a provider without affecting consumers.
-
It is fun! ;-)
If you have read my other object builder posts, you will be familar with the code below:
static void Main(string[] args)
{
IDog dog = Builder.Build<IDog>();
Console.WriteLine("Legs: {0}, Talk: {1}.", dog.Legs, dog.Talk);
ICat cat = Builder.Build<ICat>();
Console.WriteLine("Legs: {0}, Talk: {1}.", cat.Legs, cat.Talk);
Console.ReadLine();
}
You may have noticed that we no longer reference the Dog or Cat classes - all we deal with is the IDog and ICat interfaces. So, how are we going to tell the object builder to build a Dog when it is asked for an IDog? We already have the custom configuration section, so we just need to read it and swap types as necessary.
We are adding new functionality, so lets add a new class:
internal static class TypeSubstitutor
As with the factory configuration, we need to get a reference to the type substitution configuration:
private static TypeSubstitutionConfiguration _typeSubstitutionConfig;
static TypeSubstitutor()
{
//load the type config
_typeSubstitutionConfig = ConfigurationManager.GetSection(
TypeSubstitutionConfiguration.TypeSubstitutionConfigurationName)
as TypeSubstitutionConfiguration;
//null check
if (_typeSubstitutionConfig == null)
{
throw new ApplicationException(Resources.CantLoadTypeCfg);
}
}
Now that we have the settings, we can create a method which can be called whenever we need to swap types:
internal static Type GetReplacement(Type originalType)
{
Type returnType = originalType;
//get the replacement type if needed
if (_typeSubstitutionConfig.Substitutions.ContainsKey(originalType.FullName))
{
returnType = GetTypeFromConfig(originalType);
}
//check the type
CheckType(returnType);
//now that we know it is valid, return it
return returnType;
}
As you can see, we only swap the type if there is a corresponding item in the configuration. We always check the type (to ensure that it is not an interface or an abstract class). To get the replacement type, we use a method similar to how we got the factory type:
private static Type GetTypeFromConfig(Type originalType)
{
//get the substitution info
TypeSubstitutionItem subs =
_typeSubstitutionConfig.Substitutions.GetItemByKey(originalType.FullName);
//get the type
Type replacementType =
Assembly.Load(subs.ReplacementAssembly).GetType(subs.ReplacementType);
//null check
if (replacementType == null)
{
throw new ApplicationException(string.Format(
Resources.CantFindType, subs.ReplacementType));
}
return replacementType;
}
Thats all there is to our new TypeSubstitutor class, all we need to do now is refactor the InstanceManufacturer<T>.CreateInstance method. We just change this line:
Type typeToCreate = typeof(T);
To this:
Type typeToCreate = TypeSubstitutor.GetReplacement(typeof(T));
Thats it! We can now change our implementations whenever we want and however we want - as long as they implement the appropriate interfaces, the consuming code should still work.
Source code: ObjectBuilder_Substitution.zip (77.39 kb)
32c37419-ba78-45d0-8ae1-f7b183302c12|1|5.0