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.
Para que comecemos de forma adequada, vamos escrever um pequeno teste de aceitação. Veja:
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:
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:
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!
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.
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?!
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:
Agora, meus testes:
Era isso!
Pingback: Mais um pequeno refactoring « Elemar DEV