Herramienta para romper dependencias en ensamblados .NET
mediante la refactorización de su código intermedio.
Presentado por:Jair Cazarin Villanueva
Bajo la supervisión de:Dr. Mauricio Osorio.
Dr. Mircea Trofin.
Universidad de las Américas, Puebla
Agenda.
• Contexto del Problema.– Programación Orientada a Objetos.– Dependencias.
• Objetivos.• Alcances y Limitaciones.• Análisis del Problema.• Diseño e Implementación.• Resultado y Pruebas.• Conclusiones.
Programación Orientada a Objetos
“El paradigma de la programación orientada a objetos, también conocida como POO, es la que usa objetos y sus interacciones para diseñar aplicaciones.”
Características Principales
Flexible
Fácil de mantener.
Dependencias.
Tipos de dependencias.
Solución: Refactorizar.
“Refactorizar es el proceso de cambiar un software de tal forma que el comportamiento externo no cambia, más bien, se mejora su estructura interna.”
• Desafortunadamente, la refactorización es un proceso que se aplica cuando se tiene acceso al código fuente.
• Ignorando las veces cuando:
Objetivo General.
• Investigar la refactorización de ensamblados binarios con el objetivo de mejorar la reusabilidad y capacidad de resolución de frameworks
Objetivo General.
• Para lograr lo anterior, se desarrolló una herramienta que refactoriza ensamblados existentes, con el objetivo de romper las dependencias contenidas entre distintas clases, y de esta forma hacer posible satisfacer estas dependencias con otros tipos, mediante la mejora de la modularización y extensibilidad de los componentes.
Afterex = Extensibility after-the-fact.
Alcances y Limitaciones.
• .NET Framework y ensamblados .NET.• Dependencias contenidas y directas.• Solución completa para el escenario de
dependencias contenidas.• En el caso de las dependencias directas solo se
abordó el tema y se hicieron los primeros experimentos con los casos de parámetros.
• Aplicación basada en consola.
Análisis del problema.
• Metodología ágil.• Definición de enfoques y tecnologías a usar.
Dependencia Contenida.
• Si tenemos un DLL D con una clase d, y un DLL C con una clase c, una dependencia contenida de D a C, sería si encontráramos instrucciones como al siguiente en clases de d:
• c someVariable = new c(…Parameters…);• ic someVariable = new c(…Parameters…);
Dependencia Directa.
• C someMethod(…);• SomeType someOtherMethod(…,C parameter,
…);• C.someStaticMember(…);• class Cls:C (<-if C wasn’t sealed)• class Cls:SomeGeneric<C>
.NET Framework.
CLR
Ejecución de código administrado.
CIL.
.method static void main(){ .entrypoint .maxstack 1 ldstr "Hello world!" call void [mscorlib]System.Console::WriteLine(string) ret}
Ensamblados.
CECIL.
Dependency Injection.
• Dependency Injection es un patrón de diseño de objetos en los cuales estos son colocados por entidades externas.
• No se usa el operador new para construir objetos.
• Una forma de implementarlo es usando factories.• Esto nos da la flexibilidad de crear
implementaciones alternas especificándola usando un archivo de configuración.
Implementación
Capa de Framework.
Capa de Framework.
• El framework fue desarrollado siguiendo las mejores prácticas dictadas por el Framework Design Guidelines.
Capa de implementación.
Dependency Injection Container.
Rompiendo dependencias contenidas.
Tipo abstracto.
public Interface ISort{ void Sort(int[] list();}
Rompiendo dependencias contenidas.
Implementación concreta.
public class BubbleSort : ISort{ public void Sort(int[] list) { …
Implementation goes here…}}
Rompiendo dependencias contenidas.
Tipo dependendiente.
public class DependentType { void m1() { ISort sorter = new BubbleSort(); … sorter.Sort(numbers) …. }}
• Propiedad
private static Func<ISort> sortBaseTypeFactory = null;
Implementación del Factorypublic static Func<ISort> SortBaseTypeFactory{ get { if (null == SortBaseTypeFactory) { SettingsReader settingsReader = new SettingsReader(); string assemblyName = settingsReader.GetValue("Assembly"); string typeName = settingsReader.GetValue("Type"); string methodName = settingsReader.GetValue("Method"); string assemblyFullName =Path.Combine(Directory.GetCurrentDirectory(), assemblyName); MethodInfo method =
Assembly.LoadFile(assemblyFullName).GetType(typeName).GetMethod(methodName); sortBaseTypeFactory = () => (String)(method.Invoke(null, null)); } return baseTypeFactory; } set { sortBaseTypeFactory = value; }}
Factory Method:
public static ISort GetBubbleSortInstance()
{ return new BubbleSort();}
XML de configuración.
<Settings> <Assembly>SortFactory.dll</Assembly>
<Type>SortFactory.Factory</Type><Method>SomeMethod</Method></Settings>
Ahora instanciamos así:
ISort sorter = Factory.SortBaseTypeFactory();
En lugar de:
ISort sorter = new BubbleSort();
Nuevo componente.
public class QuickSort : ISort{ public void Sort(int[] list) { …Implementation goes here… }}
Factory Method
public static ISort GetQuickSortInstance(){ return new QuickSort();}
XML de configuración.
<Settings> <Assembly>newComponent.dll</Assembly>
<Type>QuickSortComponent</Type><Method>GetQuickSortInstance</Method></Settings>
Refactorizando instanciaciones.RefactorInstantiations(baseType, concreteType, targetAssembly)1 Foreach Type t in targetAssembly.Types2 Foreach Method m in t.Methods3 If method doesn’t has a body4 Continue5 If method doesn’t contain a variable of type6 baseType7 Continue8 Foreach Instruction i in m.Body9 If perform a new instantiation of10 concreteType11 Replace the instruction to call12 the property instead of new.
public class DependentType { void m1() { BubbleSort sorter = new
BubbleSort(); … sorter.Sort(numbers) …. }}
Refactorizando instanciaciones concretas.
RefactorInstantiations(concreteType, targetAssembly)1 newInterface ← new Interface2 Declare the operations of concreteType in
newInterface3 Change concreteType to implemente newInterface4 Perform RefactorConcreteImplementations5 the property instead of new
AND6 change the variable to be
baseType.
Dependencias Directas.
• Se logró crear una capa más abstracta para el API que define.
• Sin embargo, aún no sabemos cómo romper la dependencia completamente.
• Tampoco sabemos cómo desarrolladores terceros puedan utilizar esta capa más abstracta o tomar ventaja de ella.
Refactorizando Parámetros.RefactorParameters(baseType, concreteType, targetAssembly)1 Foreach Type t in target.AssemblyTypes2 Foreach Method m in t.Methods3 If m contain parameter of type concreteType4 Change the parameter to be baseType6 mbody ← m.MethodBody5 Make the method protected and abstract.6 Make the type to be abstract.7 Create a new type tAbstract8 Create a new assemblyAbstract9 Make tAbstract inherits from t10 Implement abstract methods of t with
mbody.11 Add tAbstract to assemblyAbstract
Renombramiento.
AssemblyRenameApproach(originalAssembly, newAssembly, originalType, Type newType)
1 oname ← originalAssembly.Name2 originalAssembly.name ← newAssembly.Name3 newAssembly.name ← oname4 otype ← originalType.name5 originalType ← newType.Name6 newType ← otype
Refactorizando Parámetros de retorno.
RefactorReturnParameters(baseType, concreteType, targetAssembly)
1 Foreach Type t in target.AssemblyTypes2 Foreach Method m in t.Methods3 If return type of m is of type concreteType4 Find which methods calls m5 Move m to the new assembly and
type.6 If the list of method that calls m > 17 Move all methods to the new
assembly8 Update calls to m.
Capa de aplicación.
Capa de aplicación.
• Extensible. Se pueden cargar nuevas reglas de refactorización.
• Fácil de cambiar a otra implementación. Ejemplo: Una interfaz gráfica.
• Genera un archivo XML con un resumen de las reglas aplicadas.
Pruebas y Resultados.
Escenario 2.
Resumiendo
• La aplicación ya no es responsable de encontrar sus dependencias.
• El contenedor se encarga de encontrar esas dependencias.
• Añadimos flexibilidad a la aplicación para futuros cambios.
• Promovimos la disminución del acoplamiento entre componentes, por lo cual facilitamos las pruebas de unidad.
Conclusiones.
Trabajo a Futuro.
• Terminar un escenario completo de las dependencias directas.
• Estudiar las dependencias indirectas y ocultas.• Crear un contenedor de dependencias para
aplicaciones existentes más robusto.• Mejorar la complejidad de los algoritmos.• Extender el API y fusionarlo con CECIL.
?