Elemar DEV

Tecnologia e desenvolvimento .net

FluentCodeMetrics – Parte 4 – TypeFilters

Olá pessoal. Tudo certo?!

Se você está chegando agora, recomendo dar uma olhada nos outros posts da série.

Nesse post, mostro como implementar um filtro na relação de “dependências” de um tipo. O objetivo é começar a construir um mecanismo que refine os resultados obtidos retirando “dependências” que sejam geradas automáticas pelo compilador e que, em consequência, temos pouca influência.

Todo o código-fonte, atualizado, está disponível no Github.

Teste de aceitação

Para que comecemos de forma adequada, vamos escrever um pequeno teste de aceitação. Veja:

image

Esse arquivo pode ser melhor visualizado no GitHib.

Pontos que eu gostaria de destacar:

Vejamos como está o arquivo com a definição das etapas:

using System;
using FluentCodeMetrics.Core;
using SharpTestsEx;
using TechTalk.SpecFlow;
using FluentCodeMetrics.Core.TypeFilters;

namespace FluentCodeMetrics.Specs
{
    [Binding]
    public class EfferentCouplingSteps
    {
        // ReSharper disable InconsistentNaming
        private int resultingCe;
        private Type workingType;
        private TypeFilter filter;
        // ReSharper restore InconsistentNaming

        [Given(@"que tenho um (.*)")]
        public void DadoQueTenhoUm(string tipo)
        {
            workingType = Type.GetType(tipo);
            workingType.Should().Not.Be.Null();
        }

        
        [Given(@"tenho um fitro de referências que desejo ignorar")]
        public void DadoTenhoUmFitroDeReferenciasQueDesejoIgnorar()
        {
        }

        [Given(@"esse filtro relaciona (.*)")]
        public void DadoEsseFiltroRelaciona(string tipo)
        {
            var type = Type.GetType(tipo);
            type.Should().Not.Be.Null();

            filter = filter == null
                         ? TypeFilter.EqualsTo(type)
                         : filter.Or(type);
        }

        [When(@"inspeciono seu acoplamento eferente considerando esse filtro")]
        public void QuandoInspecionoSeuAcoplamentoEferenteConsiderandoEsseFiltro()
        {
            resultingCe = workingType.ComputeCe(filter.Not());
        }

        [When(@"inspeciono seu acoplamento eferente")]
        public void QuandoInspecionoSeuAcoplamentoEferente()
        {
            resultingCe = workingType.ComputeCe();
        }

        [Then(@"obtenho (.*)")]
        public void EntaoObtenho(int ce)
        {
            resultingCe.Should().Be(ce);
        }
    }
}

Como pode ver, há grande “alinhamento” nas etapas que foram necessárias para essa Feature e as que já haviam sido desenvolvidas. Inclusive, há reaproveitamento.

Destaco:

  • Planejei uma sobrecarga para o método ComputeCe onde informo uma instância de TypeFilter;
  • Para cada novo tipo especificado pelo teste de aceitação, combino-o com o TypeFilter existente usando o “operador” Or.

Estrutura do TypeFilter

TypeFilter é um tipo abstrato que verifica se um tipo é, ou não, apropriado para uma operação. Veja:

using System;

namespace FluentCodeMetrics.Core.TypeFilters
{
    public abstract class TypeFilter
    {
        public abstract bool Check(Type type);

        public TypeFilter Or(TypeFilter filter)
        {
            return new OrTypeFilter(this, filter);
        }

        public TypeFilter Not()
        {
            return TypeFilter.Not(this);
        }

        public static implicit operator TypeFilter(Type type)
        {
            return TypeFilter.EqualsTo(type);
        }

        public static TypeFilter Not(TypeFilter filter)
        {
            return new NotTypeFilter(filter);
        }

        public static TypeFilter EqualsTo(Type type)
        {
            return new EqualsToTypeFilter(type);
        }
    }
}

Destaco:

  • Há um único método abstrato (Check) para verificar se o tipo atende ao filtro ou não;
  • Há dois métodos estáticos (Not e EqualsTo) para construção dos TypeFilters apropriados;
  • Há dois métodos de instância (Not e Or) que operam como operadores de combinação;
  • Há um conversor implícito para converter “tipos” em TypeFilters.

Veja a implementação de EqualsTypeFilter:

using System;

namespace FluentCodeMetrics.Core.TypeFilters
{
    public class EqualsToTypeFilter : TypeFilter
    {
        private readonly Type typeField;

        internal EqualsToTypeFilter(Type type)
        {
            typeField = type;
        }

        public override bool Check(Type type)
        {
            return typeField == type;
        }
    }
}

NotTypeFilter:

using System;
namespace FluentCodeMetrics.Core.TypeFilters
{
    public class NotTypeFilter : TypeFilter
    {
        private readonly TypeFilter filterField;
        internal NotTypeFilter(TypeFilter filter)
        {
            filterField = filter;
        }

        public override bool Check(Type type)
        {
            return !filterField.Check(type);
        }
    }
}

OrTypeFilter

using System;

namespace FluentCodeMetrics.Core.TypeFilters
{
    public class OrTypeFilter : TypeFilter
    {
        private readonly TypeFilter leftField;
        private readonly TypeFilter rightField;

        internal OrTypeFilter(TypeFilter left, TypeFilter right)
        {
            leftField = left;
            rightField = right;
        }

        public override bool Check(Type type)
        {
            return leftField.Check(type) || rightField.Check(type);
        }
    }
}

Perfeito!

Alguns Extension Methods…

Para melhorar a fluência, adicionei também alguns Extension Methods. Veja:

using System;

namespace FluentCodeMetrics.Core.TypeFilters
{
    public static class TypeExtensions
    {
        public static TypeFilter Or(this Type left, TypeFilter right)
        {
            return TypeFilter.EqualsTo(left)
                .Or(right);
        }

        public static TypeFilter Not(this Type that)
        {
            return TypeFilter.Not(that);
        }
    }
}

A idéia é melhorar a fluência para quem for “interagir” com a interface.

A sobrecaga no método de consulta

Com TypeMethods implementado, basta adicionar uma sobrecarga a rotina de consulta. Veja:

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

Ficou simples, não?!

Testes de Unidade

Como você sabe, escrevo testes de unidade para suportar meu processo de desenvolvimento da feature. Eis os testes desenvolvidos até aqui:

using System;

using SharpTestsEx;
using NUnit.Framework;

using FluentCodeMetrics.Core.TypeFilters;

namespace FluentCodeMetrics.Tests
{
    [TestFixture]
    public class TypeFilterTests
    {
        [Test]
        [TestCase(typeof(int), true)]
        [TestCase(typeof(string), false)]
        public void EqualsToInt32(Type type, bool expected)
        {
            TypeFilter
                .EqualsTo(typeof (int))
                .Check(type)
                .Should().Be(expected);
        }

        [Test]
        [TestCase(typeof(int), true)]
        [TestCase(typeof(string), true)]
        [TestCase(typeof(DateTime), false)]
        public void EqualsToInt32OrString(Type type, bool expected)
        {
            typeof(int)
                .Or(typeof(string))
                .Check(type)
                .Should().Be(expected);
        }


        [Test]
        [TestCase(typeof(int), false)]
        [TestCase(typeof(string), false)]
        [TestCase(typeof(DateTime), true)]
        public void Not_EqualsToInt32OrString(Type type, bool expected)
        {
            typeof(int)
                .Or(typeof(string))
                .Not()
                .Check(type)
                .Should().Be(expected);
               
        }
    }
}

Como pode ver, não testei os parâmetros. Como uso TDD, não implementei qualquer verificação. Você pode ajudar, certo?!

Vejamos, minhas Specs:

image

Agora, meus testes:

image

Era isso!

One Comment on “FluentCodeMetrics – Parte 4 – TypeFilters

  1. 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 06/05/2012 por em Post e marcado , .

Estatísticas

  • 427,253 hits
%d bloggers like this: