Elemar DEV

Tecnologia e desenvolvimento .net

Um pequeno exercício de Refactoring

Olá pessoal. Tudo certo?!

Se estivermos atentos ao feedback que nosso código nos dá, temos a possibilidade de melhorar nosso design.

Dois “gritos” evidentes são:

  • muitos cenários de teste para um único método – ele está sobrecarregado;
  • muitos métodos estáticos – não estamos implementando a abstração correta;

No post de hoje, mostro um refactoring no FluentCodeMetrics. O código está no GitHub.

O método a ser melhorado

Para começar, vamos dar uma olhada no método que desejamos melhorar. Veja:

public static IEnumerable<Type>
    GetReferencedTypesByNewobjInstruction(this Type that)
{
    var assembly = AssemblyCache.Load(that.Assembly.GetName().Name);
    var typeDef = assembly.MainModule.Types
        .First(type => type.FullName == that.FullName);

    return
        from method in typeDef.Methods
        from instruction in method.Body.Instructions
        where instruction.OpCode == OpCodes.Newobj
        let operand = instruction.Operand as MethodReference
        let typeFullName = operand.DeclaringType.FullName
        let type = Type.GetType(typeFullName)
        where type != null
        select type;
}

public static IEnumerable<Type>
    GetReferencedTypes(this Type that)
{

            
            
    const BindingFlags flags = BindingFlags.Static |
                                BindingFlags.Instance |
                                BindingFlags.NonPublic |
                                BindingFlags.Public;

    var typeMetaAttributeTypes =
        from attribute in that.GetCustomAttributes(true)
        select attribute.GetType();

    var fieldMetaAttributeTypes =
        from field in that.GetFields(flags)
        from attribute in field.GetCustomAttributes(true)
        select attribute.GetType();

    var methodMetaAttributeTypes =
        from method in that.GetMethods(flags)
        from attribute in method.GetCustomAttributes(true)
        select attribute.GetType();

    var methodParameterMetaAttributeTypes =
        from method in that.GetMethods(flags)
        from parameter in method.GetParameters()
        from attribute in parameter.GetCustomAttributes(true)
        select attribute.GetType();

    var fieldTypes =
        from field in that.GetFields(flags)
        select field.FieldType;

    var propertyTypes =
        from property in that.GetProperties(flags)
        select property.PropertyType;

    var methodReturnTypes =
        from method in that.GetMethods(flags)
        where method.ReturnType != typeof(void)
        select method.ReturnType;

    var methodParameterTypes =
        from method in that.GetMethods(flags)
        from parameter in method.GetParameters()
        select parameter.ParameterType;

    var ctorParameterTypes =
        from ctor in that.GetConstructors(flags)
        from parameter in ctor.GetParameters()
        select parameter.ParameterType;

    return new[] { that.BaseType }
        .Union(that.GetReferencedTypesByNewobjInstruction())
        .Union(typeMetaAttributeTypes)
        .Union(fieldMetaAttributeTypes)
        .Union(methodMetaAttributeTypes)
        .Union(methodParameterMetaAttributeTypes)
        .Union(ctorParameterTypes)
        .Union(methodParameterTypes)
        .Union(fieldTypes)
        .Union(propertyTypes)
        .Union(methodReturnTypes)
        .Distinct();
}

Como você pode ver, este método está “grande demais”. Tão grande que acabei “quebrando” a lógica em duas partes.

Primeiro refactoring – Extracting Methods

A primeira alteração, evidente, é extrair métodos. Veja o resultado:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil.Cil;
using TypeFilter = FluentCodeMetrics.Core.TypeFilters.TypeFilter;

namespace FluentCodeMetrics.Core
{
    public static class CeExtensions
    {
        public static int ComputeCe(this Type that)
        {
            return that
                .GetReferencedTypes()
                .Count();
        }

        const BindingFlags Flags = BindingFlags.Static |
                                       BindingFlags.Instance |
                                       BindingFlags.NonPublic |
                                       BindingFlags.Public;

        public static IEnumerable<Type>
            GetReferencedTypes(this Type that)
        {
            var typeMetaAttributeTypes = GetTypeMetaAttributeTypes(that);
            var fieldMetaAttributeTypes = GetFieldMetaAttributeTypes(that);
            var methodMetaAttributeTypes = GetMethodMetaAttributeTypes(that);
            var methodParameterMetaAttributeTypes = GetMethodParameterMetaAttributeTypes(that);
            var fieldTypes = GetFieldTypes(that);
            var propertyTypes = GetPropertyTypes(that);
            var methodReturnTypes = GetMethodReturnTypes(that);
            var methodParameterTypes = GetMethodParameterTypes(that);
            var ctorParameterTypes = GetCtorParameterTypes(that);

            return new[] { that.BaseType }
                .Union(that.GetReferencedTypesByNewobjInstruction())
                .Union(typeMetaAttributeTypes)
                .Union(fieldMetaAttributeTypes)
                .Union(methodMetaAttributeTypes)
                .Union(methodParameterMetaAttributeTypes)
                .Union(ctorParameterTypes)
                .Union(methodParameterTypes)
                .Union(fieldTypes)
                .Union(propertyTypes)
                .Union(methodReturnTypes)
                .Distinct();
        }

        public static IEnumerable<Type>
            GetReferencedTypesByNewobjInstruction(this Type that)
        {
            var assembly = AssemblyCache.Load(that.Assembly.GetName().Name);
            var typeDef = assembly.MainModule.Types
                .First(type => type.FullName == that.FullName);

            return
                from method in typeDef.Methods
                from instruction in method.Body.Instructions
                where instruction.OpCode == OpCodes.Newobj
                let operand = instruction.Operand as MethodReference
                let typeFullName = operand.DeclaringType.FullName
                let type = Type.GetType(typeFullName)
                where type != null
                select type;
        }

        private static IEnumerable<Type> GetCtorParameterTypes(Type that)
        {
            var ctorParameterTypes =
                from ctor in that.GetConstructors(Flags)
                from parameter in ctor.GetParameters()
                select parameter.ParameterType;
            return ctorParameterTypes;
        }

        private static IEnumerable<Type> GetMethodParameterTypes(Type that)
        {
            var methodParameterTypes =
                from method in that.GetMethods(Flags)
                from parameter in method.GetParameters()
                select parameter.ParameterType;
            return methodParameterTypes;
        }

        private static IEnumerable<Type> GetMethodReturnTypes(Type that)
        {
            var methodReturnTypes =
                from method in that.GetMethods(Flags)
                where method.ReturnType != typeof (void)
                select method.ReturnType;
            return methodReturnTypes;
        }

        private static IEnumerable<Type> GetPropertyTypes(Type that)
        {
            var propertyTypes =
                from property in that.GetProperties(Flags)
                select property.PropertyType;
            return propertyTypes;
        }

        private static IEnumerable<Type> GetFieldTypes(Type that)
        {
            var fieldTypes =
                from field in that.GetFields(Flags)
                select field.FieldType;
            return fieldTypes;
        }

        private static IEnumerable<Type> GetMethodParameterMetaAttributeTypes(Type that)
        {
            var methodParameterMetaAttributeTypes =
                from method in that.GetMethods(Flags)
                from parameter in method.GetParameters()
                from attribute in parameter.GetCustomAttributes(true)
                select attribute.GetType();
            return methodParameterMetaAttributeTypes;
        }

        private static IEnumerable<Type> GetMethodMetaAttributeTypes(Type that)
        {
            var methodMetaAttributeTypes =
                from method in that.GetMethods(Flags)
                from attribute in method.GetCustomAttributes(true)
                select attribute.GetType();
            return methodMetaAttributeTypes;
        }

        private static IEnumerable<Type> GetFieldMetaAttributeTypes(Type that)
        {
            var fieldMetaAttributeTypes =
                from field in that.GetFields(Flags)
                from attribute in field.GetCustomAttributes(true)
                select attribute.GetType();
            return fieldMetaAttributeTypes;
        }

        private static IEnumerable<Type> GetTypeMetaAttributeTypes(Type that)
        {
            return
                from attribute in that.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public static int ComputeCe(this Type that, TypeFilter filter)
        {
            return that
                .GetReferencedTypes()
                .Count(filter.Check);
        }
    }
}

Segundo refactoring – Extraindo classe

A classe CeExtensions está “estressada”. A maior parte dos métodos está “pesquisando” tipos. Extrair esses métodos para outra classe parece a saída evidente. Veja o resultado:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace FluentCodeMetrics.Core
{
    public static class ReferencesInspector
    {
        private const BindingFlags Flags = BindingFlags.Static |
                                           BindingFlags.Instance |
                                           BindingFlags.NonPublic |
                                           BindingFlags.Public;

        public static IEnumerable<Type>
            GetReferencedTypesByNewobjInstruction(this Type that)
        {
            var assembly = AssemblyCache.Load(that.Assembly.GetName().Name);
            var typeDef = assembly.MainModule.Types
                .First(type => type.FullName == that.FullName);

            return
                from method in typeDef.Methods
                from instruction in method.Body.Instructions
                where instruction.OpCode == OpCodes.Newobj
                let operand = instruction.Operand as MethodReference
                let typeFullName = operand.DeclaringType.FullName
                let type = Type.GetType(typeFullName)
                where type != null
                select type;
        }

        public static IEnumerable<Type> GetCtorParameterTypes(Type that)
        {
            return    
                from ctor in that.GetConstructors(Flags)
                from parameter in ctor.GetParameters()
                select parameter.ParameterType;
        }

        public static IEnumerable<Type> GetMethodParameterTypes(Type that)
        {
            return    
                from method in that.GetMethods(Flags)
                from parameter in method.GetParameters()
                select parameter.ParameterType;
        }

        public static IEnumerable<Type> GetMethodReturnTypes(Type that)
        {
            return    
                from method in that.GetMethods(Flags)
                where method.ReturnType != typeof(void)
                select method.ReturnType;
        }

        public static IEnumerable<Type> GetPropertyTypes(Type that)
        {
            return    
                from property in that.GetProperties(Flags)
                select property.PropertyType;
        }

        public static IEnumerable<Type> GetFieldTypes(Type that)
        {
            return    
                from field in that.GetFields(Flags)
                select field.FieldType;
        }

        public static IEnumerable<Type> GetMethodParameterMetaAttributeTypes(Type that)
        {
            return    
                from method in that.GetMethods(Flags)
                from parameter in method.GetParameters()
                from attribute in parameter.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public static IEnumerable<Type> GetMethodMetaAttributeTypes(Type that)
        {
            return    
                from method in that.GetMethods(Flags)
                from attribute in method.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public static IEnumerable<Type> GetFieldMetaAttributeTypes(Type that)
        {
            return
                from field in that.GetFields(Flags)
                from attribute in field.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public static IEnumerable<Type> GetTypeMetaAttributeTypes(Type that)
        {
            return
                from attribute in that.GetCustomAttributes(true)
                select attribute.GetType();
        }
    }
}

ComputeCe ficou bem menor. Veja:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil.Cil;
using TypeFilter = FluentCodeMetrics.Core.TypeFilters.TypeFilter;

namespace FluentCodeMetrics.Core
{
    public static class CeExtensions
    {
        public static int ComputeCe(this Type that)
        {
            return that
                .GetReferencedTypes()
                .Count();
        }

        
        public static IEnumerable<Type>
            GetReferencedTypes(this Type that)
        {
            var typeMetaAttributeTypes = ReferencesInspector.GetTypeMetaAttributeTypes(that);
            var fieldMetaAttributeTypes = ReferencesInspector.GetFieldMetaAttributeTypes(that);
            var methodMetaAttributeTypes = ReferencesInspector.GetMethodMetaAttributeTypes(that);
            var methodParameterMetaAttributeTypes = ReferencesInspector.GetMethodParameterMetaAttributeTypes(that);
            var fieldTypes = ReferencesInspector.GetFieldTypes(that);
            var propertyTypes = ReferencesInspector.GetPropertyTypes(that);
            var methodReturnTypes = ReferencesInspector.GetMethodReturnTypes(that);
            var methodParameterTypes = ReferencesInspector.GetMethodParameterTypes(that);
            var ctorParameterTypes = ReferencesInspector.GetCtorParameterTypes(that);

            return new[] { that.BaseType }
                .Union(that.GetReferencedTypesByNewobjInstruction())
                .Union(typeMetaAttributeTypes)
                .Union(fieldMetaAttributeTypes)
                .Union(methodMetaAttributeTypes)
                .Union(methodParameterMetaAttributeTypes)
                .Union(ctorParameterTypes)
                .Union(methodParameterTypes)
                .Union(fieldTypes)
                .Union(propertyTypes)
                .Union(methodReturnTypes)
                .Distinct();
        }


        public static int ComputeCe(this Type that, TypeFilter filter)
        {
            return that
                .GetReferencedTypes()
                .Count(filter.Check);
        }
    }
}

Terceiro refactoring – No Static!

Não há razões justificáveis para manter nossos métodos de “consulta ao código” estáticos. Veja:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace FluentCodeMetrics.Core
{
    public class ReferencesInspector
    {
        // ReSharper disable InconsistentNaming
        private readonly Type workingType;
        // ReSharper restore InconsistentNaming
        
        public ReferencesInspector(Type type)
        {
            workingType = type;
        }

        public static ReferencesInspector For<T>()
        {
            return For(typeof(T));
        }

        public static ReferencesInspector For(Type type)
        {
            return new ReferencesInspector(type);
        }

        private const BindingFlags Flags = BindingFlags.Static |
                                           BindingFlags.Instance |
                                           BindingFlags.NonPublic |
                                           BindingFlags.Public;

        public IEnumerable<Type>
            ByNewobjInstruction()
        {
            var assembly = AssemblyCache.Load(workingType.Assembly.GetName().Name);
            var typeDef = assembly.MainModule.Types
                .First(type => type.FullName == workingType.FullName);

            return
                from method in typeDef.Methods
                from instruction in method.Body.Instructions
                where instruction.OpCode == OpCodes.Newobj
                let operand = instruction.Operand as MethodReference
                let typeFullName = operand.DeclaringType.FullName
                let type = Type.GetType(typeFullName)
                where type != null
                select type;
        }

        public IEnumerable<Type> 
            ByCtorParametersTypes()
        {
            return    
                from ctor in workingType.GetConstructors(Flags)
                from parameter in ctor.GetParameters()
                select parameter.ParameterType;
        }

        public IEnumerable<Type> 
            ByMethodsParametersTypes()
        {
            return    
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                select parameter.ParameterType;
        }

        public IEnumerable<Type> 
            ByMethodsReturnTypes()
        {
            return    
                from method in workingType.GetMethods(Flags)
                where method.ReturnType != typeof(void)
                select method.ReturnType;
        }

        public IEnumerable<Type> 
            ByPropertiesTypes()
        {
            return    
                from property in workingType.GetProperties(Flags)
                select property.PropertyType;
        }

        public IEnumerable<Type> 
            ByFieldsTypes()
        {
            return    
                from field in workingType.GetFields(Flags)
                select field.FieldType;
        }

        public IEnumerable<Type> 
            ByParametersMetaAttributesTypes()
        {
            return    
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                from attribute in parameter.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public IEnumerable<Type> 
            OfMethodsMetaAttributesTypes()
        {
            return    
                from method in workingType.GetMethods(Flags)
                from attribute in method.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public IEnumerable<Type> 
            OfFieldsMetaAttributesTypes()
        {
            return
                from field in workingType.GetFields(Flags)
                from attribute in field.GetCustomAttributes(true)
                select attribute.GetType();
        }

        public IEnumerable<Type> OfMetaAttributesTypes()
        {
            return
                from attribute in workingType.GetCustomAttributes(true)
                select attribute.GetType();
        }
    }
}

Ainda indeciso quanto ao nome dos métodos. Mas, no caminho. Vejamos como ficou CeExtensions:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil.Cil;
using TypeFilter = FluentCodeMetrics.Core.TypeFilters.TypeFilter;

namespace FluentCodeMetrics.Core
{
    public static class CeExtensions
    {
        public static int ComputeCe(this Type that)
        {
            return that
                .GetReferencedTypes()
                .Count();
        }

        
        public static IEnumerable<Type>
            GetReferencedTypes(this Type that)
        {
            var inspector = ReferencesInspector.For(that);
            var referencedTypesByNewobjInstruction = inspector.OfNewobjInstruction();
            var typeMetaAttributeTypes = inspector.OfMetaAttributesTypes();
            var fieldMetaAttributeTypes = inspector.OfFieldsMetaAttributesTypes();
            var methodMetaAttributeTypes = inspector.OfMethodsMetaAttributesTypes();
            var methodParameterMetaAttributeTypes = inspector.OfParametersMetaAttributesTypes();
            var fieldTypes = inspector.OfFieldsTypes();
            var propertyTypes = inspector.OfPropertiesTypes();
            var methodReturnTypes = inspector.OfMethodsReturnTypes();
            var methodParameterTypes = inspector.OfMethodsParametersTypes();
            var ctorParameterTypes = inspector.OfCtorParametersTypes();

            return new[] { that.BaseType }
                .Union(referencedTypesByNewobjInstruction)
                .Union(typeMetaAttributeTypes)
                .Union(fieldMetaAttributeTypes)
                .Union(methodMetaAttributeTypes)
                .Union(methodParameterMetaAttributeTypes)
                .Union(ctorParameterTypes)
                .Union(methodParameterTypes)
                .Union(fieldTypes)
                .Union(propertyTypes)
                .Union(methodReturnTypes)
                .Distinct();
        }


        public static int ComputeCe(this Type that, TypeFilter filter)
        {
            return that
                .GetReferencedTypes()
                .Count(filter.Check);
        }
    }
}

Quarto Refactoring – Criando interface fluente

Gosto da idéia de ter uma interface fluente para essa “consulta”.

Para começar, resolvi melhorar o retorno dos métodos de consulta. Veja:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FluentCodeMetrics.Core
{
    public class ReferencedTypes : 
        IEnumerable<Type>
    {
        private readonly IEnumerable<Type> sourceField;
        private readonly Type originalField;

        internal ReferencedTypes(IEnumerable<Type> source, Type original)
        {
            sourceField = source;
            originalField = original;
        }

        public IEnumerator<Type> GetEnumerator()
        {
            return sourceField.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return sourceField.GetEnumerator();
        }

        public ReferencesInspector And
        {
            get { return new ReferencesInspector(originalField, sourceField); }
        }
    }
}

Agora, aprimorar ReferencesInspector. Veja como finalmente melhoro o nome dos métodos.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace FluentCodeMetrics.Core
{
    public class ReferencesInspector
    {
        // ReSharper disable InconsistentNaming
        private readonly Type workingType;
        private readonly IEnumerable<Type> other;
        // ReSharper restore InconsistentNaming

        internal ReferencesInspector(Type type, IEnumerable<Type> other) 
        {
            workingType = type;
            this.other = other;
        }
        
        private ReferencesInspector(Type type) 
           
        {
            workingType = type;
            other = new Type[] {};
        }

        public static ReferencesInspector For<T>()
        {
            return For(typeof(T));
        }

        public static ReferencesInspector For(Type type)
        {
            return new ReferencesInspector(type);
        }

        private const BindingFlags Flags = BindingFlags.Static |
                                           BindingFlags.Instance |
                                           BindingFlags.NonPublic |
                                           BindingFlags.Public;

        public ReferencedTypes
            FromBaseType()
        {
            return new ReferencedTypes(
                new[] { workingType.BaseType },
                workingType
                );
        }

        public ReferencedTypes
            FromNewobjInstructions()
        {
            var assembly = AssemblyCache.Load(workingType.Assembly.GetName().Name);
            var typeDef = assembly.MainModule.Types
                .First(type => type.FullName == workingType.FullName);

            var source = 
                from method in typeDef.Methods
                from instruction in method.Body.Instructions
                where instruction.OpCode == OpCodes.Newobj
                let operand = instruction.Operand as MethodReference
                let typeFullName = operand.DeclaringType.FullName
                let type = Type.GetType(typeFullName)
                where type != null
                select type;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromCtorParameters()
        {
            var source =    
                from ctor in workingType.GetConstructors(Flags)
                from parameter in ctor.GetParameters()
                select parameter.ParameterType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsParameters()
        {
            var source =    
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                select parameter.ParameterType;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsReturnTypes()
        {
            var source =     
                from method in workingType.GetMethods(Flags)
                where method.ReturnType != typeof(void)
                select method.ReturnType;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromProperties()
        {
            var source =     
                from property in workingType.GetProperties(Flags)
                select property.PropertyType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromFields()
        {
            var source =    
                from field in workingType.GetFields(Flags)
                select field.FieldType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromParametersMetaAttributes()
        {
            var source =   
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                from attribute in parameter.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsMetaAttributes()
        {
            var source =   
                from method in workingType.GetMethods(Flags)
                from attribute in method.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromFieldsMetaAttributes()
        {
            var source = 
                from field in workingType.GetFields(Flags)
                from attribute in field.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMetaAttributes()
        {
            var source = 
                from attribute in workingType.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }
    }
}

Por fim, melhoro o consumo de tais métodos. Veja:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil.Cil;
using TypeFilter = FluentCodeMetrics.Core.TypeFilters.TypeFilter;

namespace FluentCodeMetrics.Core
{
    public static class CeExtensions
    {
        public static int ComputeCe(this Type that)
        {
            return that
                .GetReferencedTypes()
                .Count();
        }

        
        public static ReferencedTypes
            GetReferencedTypes(this Type that)
        {
            return ReferencesInspector.For(that)
                .FromBaseType()
                .And.FromNewobjInstructions()
                .And.FromMetaAttributes()
                .And.FromFieldsMetaAttributes()
                .And.FromMethodsMetaAttributes()
                .And.FromParametersMetaAttributes()
                .And.FromFields()
                .And.FromProperties()
                .And.FromMethodsReturnTypes()
                .And.FromMethodsParameters()
                .And.FromCtorParameters();
        }


        public static int ComputeCe(this Type that, TypeFilter filter)
        {
            return that
                .GetReferencedTypes()
                .Count(filter.Check);
        }
    }
}

Quinto Refactoring – Especialidades…

Por que, afinal de contas, GerReferencedTypes está em ComputeCe?! Mudemos ela de nome e de lugar.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace FluentCodeMetrics.Core
{
    public class ReferencesInspector
    {
        // ReSharper disable InconsistentNaming
        private readonly Type workingType;
        private readonly IEnumerable<Type> other;
        // ReSharper restore InconsistentNaming

        internal ReferencesInspector(Type type, IEnumerable<Type> other) 
        {
            workingType = type;
            this.other = other;
        }
        
        private ReferencesInspector(Type type) 
           
        {
            workingType = type;
            other = new Type[] {};
        }

        public static ReferencesInspector For<T>()
        {
            return For(typeof(T));
        }

        public static ReferencesInspector For(Type type)
        {
            return new ReferencesInspector(type);
        }

        private const BindingFlags Flags = BindingFlags.Static |
                                           BindingFlags.Instance |
                                           BindingFlags.NonPublic |
                                           BindingFlags.Public;

        public ReferencedTypes
            FromBaseType()
        {
            return new ReferencedTypes(
                new[] { workingType.BaseType },
                workingType
                );
        }

        public ReferencedTypes
            FromNewobjInstructions()
        {
            var assembly = AssemblyCache.Load(workingType.Assembly.GetName().Name);
            var typeDef = assembly.MainModule.Types
                .First(type => type.FullName == workingType.FullName);

            var source = 
                from method in typeDef.Methods
                from instruction in method.Body.Instructions
                where instruction.OpCode == OpCodes.Newobj
                let operand = instruction.Operand as MethodReference
                let typeFullName = operand.DeclaringType.FullName
                let type = Type.GetType(typeFullName)
                where type != null
                select type;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromCtorParameters()
        {
            var source =    
                from ctor in workingType.GetConstructors(Flags)
                from parameter in ctor.GetParameters()
                select parameter.ParameterType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsParameters()
        {
            var source =    
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                select parameter.ParameterType;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsReturnTypes()
        {
            var source =     
                from method in workingType.GetMethods(Flags)
                where method.ReturnType != typeof(void)
                select method.ReturnType;

            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromProperties()
        {
            var source =     
                from property in workingType.GetProperties(Flags)
                select property.PropertyType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromFields()
        {
            var source =    
                from field in workingType.GetFields(Flags)
                select field.FieldType;
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromParametersMetaAttributes()
        {
            var source =   
                from method in workingType.GetMethods(Flags)
                from parameter in method.GetParameters()
                from attribute in parameter.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMethodsMetaAttributes()
        {
            var source =   
                from method in workingType.GetMethods(Flags)
                from attribute in method.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromFieldsMetaAttributes()
        {
            var source = 
                from field in workingType.GetFields(Flags)
                from attribute in field.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes 
            FromMetaAttributes()
        {
            var source = 
                from attribute in workingType.GetCustomAttributes(true)
                select attribute.GetType();
            return new ReferencedTypes(other.Union(source), workingType);
        }

        public ReferencedTypes
            All()
        {
            return new ReferencedTypes(FromBaseType()
                .And.FromNewobjInstructions()
                .And.FromMetaAttributes()
                .And.FromFieldsMetaAttributes()
                .And.FromMethodsMetaAttributes()
                .And.FromParametersMetaAttributes()
                .And.FromFields()
                .And.FromProperties()
                .And.FromMethodsReturnTypes()
                .And.FromMethodsParameters()
                .And.FromCtorParameters()
                .Distinct(), workingType);
        }
    }
}

Ela virou All, dentro de ReferencesInspector.

Também melhoremos nossa interface fluente para facilitar filtros. Veja:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentCodeMetrics.Core.TypeFilters;

namespace FluentCodeMetrics.Core
{
    public class ReferencedTypes : 
        IEnumerable<Type>
    {
        private readonly IEnumerable<Type> sourceField;
        private readonly Type originalField;

        internal ReferencedTypes(IEnumerable<Type> source, Type original)
        {
            sourceField = source;
            originalField = original;
        }

        public IEnumerator<Type> GetEnumerator()
        {
            return sourceField.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return sourceField.GetEnumerator();
        }

        public ReferencesInspector And
        {
            get
            {
                return new ReferencesInspector(
                    originalField, 
                    sourceField
                    );
            }
        }

        public ReferencedTypes FilterBy(TypeFilter filter)
        {
            return new ReferencedTypes(
                sourceField.Where(filter.Check),
                originalField
                );
        }
    }
}

Finalmente, como ficou ComputeCe.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Mono.Cecil;
using FluentCodeMetrics.Core.Cecil;
using Mono.Cecil.Cil;
using TypeFilter = FluentCodeMetrics.Core.TypeFilters.TypeFilter;

namespace FluentCodeMetrics.Core
{
    public static class CeExtensions
    {
        public static int ComputeCe(this Type that)
        {
            return ReferencesInspector.For(that)
                .All()
                .Count();
        }



        public static int ComputeCe(this Type that, TypeFilter filter)
        {
            return ReferencesInspector.For(that)
                .All()
                .FilterBy(filter)
                .Count();
        }
    }
}

Efeito colateral – ajustando os testes

Como mudamos o design, nossos testes de unidade precisaram ser revistos. Veja:

using System;
using System.Linq;
using System.Collections.Generic;
using FluentCodeMetrics.Core.TypeFilters;
using NUnit.Framework;
using SharpTestsEx;
using FluentCodeMetrics.Core;
using Samples;

namespace FluentCodeMetrics.Tests
{
    [TestFixture]
    // ReSharper disable InconsistentNaming
    public class ReferencesInspectorTests
    {
        private readonly List<Type> common = new List<Type>()
        {
            typeof (System.Runtime.TargetedPatchingOptOutAttribute),
            typeof (System.Security.SecuritySafeCriticalAttribute),
            typeof (System.Runtime.ConstrainedExecution.ReliabilityContractAttribute),
            typeof (object),
            typeof (string),
            typeof (bool),
            typeof (int),
            typeof (Type)
        };

        [Test]
        public void All_EmptyClass()
        {
            ReferencesInspector.For(typeof(EmptyClass)).All()
                .Should().Have.SameValuesAs(
                    common
                );
        }

        [Test]
        public void All_SingleField()
        {
            ReferencesInspector.For(typeof(SingleField)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(DateTime) 
                    })
                 );
        }

        [Test]
        public void All_SingleProperty()
        {
            ReferencesInspector.For(typeof(SingleProperty)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(DateTime),
                    typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)
                    })
                 );
        }

        [Test]
        public void All_SingleNonAutoProperty()
        {
            ReferencesInspector.For(typeof(SingleNonAutoProperty)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(DateTime) 
                    })
                 );
        }

        [Test]
        public void All_FeeMethod()
        {
            ReferencesInspector.For(typeof(FeeMethod)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(Fee) 
                    })
                 );
        }

        [Test]
        public void All_SingleArgVoidMethod()
        {
            ReferencesInspector.For(typeof(SingleArgVoidMethod)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(Fee) 
                    })
                 );
        }

        [Test]
        public void All_SingleArgCtor()
        {
            ReferencesInspector.For(typeof(SingleArgCtor)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(Fee) 
                    })
                 );
        }

        [Test]
        public void All_ExceptionRaiser()
        {
            ReferencesInspector.For(typeof(ExceptionRaiser)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(Exception) 
                    })
                 );
        }

        [Test]
        public void All_Attributes()
        {
            ReferencesInspector.For(typeof(Attributes)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new []
                    {
                    typeof(SerializableAttribute),  // Class Attribute
                    typeof(NonSerializedAttribute), // Field Attribute
                    typeof(FooAttribute),           // MethodAttribute
                    typeof(FooAttribute2)           // Parameter Attribute
                    })
                 );
        }

        [Test]
        public void All_StaticProperty()
        {
            ReferencesInspector.For(typeof(StaticMember)).All()
                .Should().Have.SameValuesAs(
                    common.Union(new[]
                    {
                    typeof(DateTime),
                    typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)
                    })
                 );
        }
    }
    // ReSharper restore InconsistentNaming
}

Resultados positivos

Todo o refactoring melhorou a testabilidade e, em consequência, o design. O código ficou mais reutilizável e inteligente.

Por agora, era isso.

4 Comentários em “Um pequeno exercício de Refactoring

  1. tpadilha
    09/05/2012

    Show de bola o post! Muitas vezes encontramos códigos que precisam de refactoring e os devs não entendem porque é necessário.

  2. tpadilha
    09/05/2012

    Reblogged this on Tiago Padilha's Blog.

  3. Pingback: FluentCodeMetrics – Parte 5 – Reconhecendo dependências em métodos/propriedades estáticas e ignorando Nested Types « Elemar DEV

  4. Pingback: Mais um pequeno refactoring « Elemar DEV

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

Você está comentando usando sua conta WordPress.com. Sair / Mudar )

Imagem do Twitter

Você está comentando usando sua conta Twitter. Sair / Mudar )

Foto do Facebook

Você está comentando usando sua conta Facebook. Sair / Mudar )

Conectando a %s

Informação

Publicado às 08/05/2012 por em Post e marcado .

Estatísticas

  • 430,036 hits
%d bloggers like this: