Entre las cosas interesantes que muchos hacemos a diario (o bueno, configuramos a diario) y que muy pocas veces nos preguntamos ¿cómo lo hicieron? esta el uso de handlers o providers en la .Net Framework. Por ejemplo, muchas veces vemos algo como:
<viewStatePersisters default="cachePersister">
<persisters>
<add name="pagePersister" type="Rioshu.Web.ViewStateHandlers.TinyPagePersister, Rioshu.Web.ViewStateHandlers" />
<add name="cachePersister" type="Rioshu.Web.ViewStateHandlers.WebCachePersister, Rioshu.Web.ViewStateHandlers" />
</persisters>
</viewStatePersisters>
Y asumimos correctamente que la framework tomará esa información e instanciará el manejador o clase correspondiente que hemos indicado por su Full Qualified Name. ¿Cómo hace la framework para cargar el tipo correcto en el assembly correcto y luego instanciarlo?. La respuesta es simple, una mezcla de propiedades del tipo del objeto y a un amigo llamado “Activator”.
Cuando me refiero al “tipo del objeto” no me refiero a si es Integer, String u Object. En la .Net Framework existe un “objeto especial” cuya responsabilidad es brindar información acerca del “tipo”, este objeto es System.Type, bien, Type tiene un método especial llamado GetType, al cual podemos pasarle el Full Qualified Name de la clase y el .Net Assembly and Type Loader intentará cargarlo.
public class TypeLoaderFacts
{
private const string ExternalTypeName = "ExternalLibrary.ExternalServiceImplementation, ExternalLibrary";
[Fact]
public void It_can_load_a_type_by_his_fqn()
{
var type = Type.GetType(ExternalTypeName);
Assert.Equal(typeof(ExternalServiceImplementation), type);
}
}
Bien, con cargar el tipo no hacemos mucho, es hora de inicializarlo.
public class TypeLoaderFacts
{
private const string ExternalTypeName = "ExternalLibrary.ExternalServiceImplementation, ExternalLibrary";
private Type ExternalType;
public TypeLoaderFacts()
{
ExternalType = Type.GetType(ExternalTypeName);
}
[Fact]
public void It_can_load_a_type_by_his_fqn()
{
Assert.Equal(typeof(ExternalServiceImplementation), ExternalType);
}
[Fact]
public void It_can_ctor_a_type()
{
var instance = Activator.CreateInstance(ExternalType);
Assert.NotNull(instance);
}
}
Pero como ustedes siguen también buenas prácticas, recordaran que es buena idea programar en base a interface y no a una clase concreta, así que podemos preguntarle al tipo si implementa la clase (algo que vamos a hacer usando un pequeño método de extensión).
public class TypeLoaderFacts
{
private const string ExternalTypeName = "ExternalLibrary.ExternalServiceImplementation, ExternalLibrary";
private Type ExternalType;
public TypeLoaderFacts()
{
ExternalType = Type.GetType(ExternalTypeName);
}
[Fact]
public void It_can_load_a_type_by_his_fqn()
{
Assert.Equal(typeof(ExternalServiceImplementation), ExternalType);
}
[Fact]
public void It_can_ctor_a_type()
{
var instance = Activator.CreateInstance(ExternalType);
Assert.NotNull(instance);
}
[Fact]
public void It_implements_our_interface()
{
Assert.True(ExternalType.IsImplementationOf<IExternalService>());
}
}
Bien, les queda de tarea el averiguar como crear el extension method en cuestion :)
Como ven es sumamente fácil llegar a implementar dynamic loaders en base a un archivo de configuración de esta manera (‘ala’ web.config de asp.net o NHibernate).
Como siempre cualquier comentario estamos a un mensaje de distancia!