Tag Archives: boas práticas

Usando Builder Pattern com Interfaces Fluentes

Boa noite, tudo certo? :)

Acabei de passar por uma situação bastante interessante onde apliquei uma solução que eu achei bastante elegante. Gostaria de compartilhá-la aqui no Blog na esperança de que isso possa ajudar alguém. Vou utilizar uma exemplo hipotético,  porém parecido com a situação real.

A situação atual

Um time está criando uma aplicação nova e decide escrever seu próprio mecanismo de logging.
Em um determinado momento foi criado um objeto de valor – que representa uma entrada de log – conforme a seguinte definição:

public class LogEntry
{
    public LogLevel Level { get; set; }
    public string Message { get; set; }
    public DateTime DateTime { get; set; }
    public string Url { get; set; }
}

public enum LogLevel
{
    Warning,
    Information,
    Error
}

Alguém consegue achar um problema no código acima?

Teeeeempo!

1
2
3

10

Vamos imaginar que nosso framework de log cria uma instancia desta classe em um determinado local e depois repassa para uma classe de filtro conforme o código abaixo.

public class OnlyWarningFilter : IFilter
{
    public bool ShouldLog(LogEntry entry)
    {
        return entry.Level.Equals(LogLevel.Warning);
    }
}

Até ai tudo bem, este filtro vai fazer com que grave apenas os logs do tipo Warning. O problema é que estamos dando a oportunidade para esta classe simplesmente alterar completamente qualquer detalhe da LogEntry. Não só no filtro como também em qualquer local em que esta instância for repassada. Isto acontece pois definimos todas as nossas propriedades como públicas, tanto o get quanto o set. Olha só o que algum desenvolvedor pode fazer dentro do filtro.

public class OnlyWarningFilter : IFilter
{
    public bool ShouldLog(LogEntry entry)
    {
        entry.Message = "All your base are belong to us!";
        entry.DateTime = DateTime.Now.AddDays(-123);
        return entry.Level.Equals(LogLevel.Warning);
    }
}

Está instalado o chaos! Todos os nossos logs terão um conteúdo indesejado ao invés de uma mensagem informativa e confiável. Isto foi apenas um exemplo, é muito provável que ninguém faça isso, mas se pudéssemos prevenir seria melhor, não acha? A solução mais obvia é marcar o set das propriedades como private e criar um construtor público recebendo as quatro propriedades. Ficaria assim.

public class LogEntry
{
    public LogLevel Level { get; private set; }
    public string Message { get; private set; }
    public DateTime DateTime { get; private set; }
    public string Url { get; private set; }

    public LogEntry(LogLevel level, string message, DateTime dateTime, string url)
    {
        this.Level = level;
        this.Message = message;
        this.DateTime = dateTime;
        this.Url = url;
    }
}

Problema resolvido. Se você se sente satisfeito por aqui, tudo bem, mas eu vou mais além!

Neste caso temos apenas quatro propriedades e o construtor ainda está bastante enxuto. Apesar de que, na minha opinião, quatro parâmetros no construtor já começa a ficar feio. Toda nova propriedade faria o construtor crescer mais e mais, o que é certamente algo indesejado.

A solução aplicada

Com certeza já ouviram falar que Design Pattern deve ser utilizado quando surge um problema já conhecido, este exemplo é a prova disto. Temos um problema e há um grupo de padrões exclusivos para criação de objetos. Vamos utilizar o Builder Pattern com algumas pitadas de Interfaces Fluente. O resultado é excelente. Para mais detalhes sobre estas duas técnicas, utilize os links acima, vou direto para a solução e explicação.

public class LogEntry
{
    public LogLevel Level { get; private set; }
    public string Message { get; private set; }
    public DateTime DateTime { get; private set; }
    public string Url { get; private set; }

    private LogEntry(Builder builder)
    {
        this.Level = builder.Level;
        this.Message = builder.Message;
        this.DateTime= builder.DateTime;
        this.Url = builder.Url;
    }

    public class Builder
    {
        public LogLevel Level { get; private set; }
        public string Message { get; private set; }
        public DateTime DateTime { get; private set; }
        public string Url { get; private set; }

        public Builder AsError()
        {
            return WithLevel(LogLevel.Error);
        }

        public Builder AsInformation()
        {
            return WithLevel(LogLevel.Information);
        }

        public Builder AsWarning()
        {
            return WithLevel(LogLevel.Warning);
        }

        public Builder WithLevel(LogLevel level)
        {
            this.Level = level;
            return this;
        }

        public Builder MessageIs(string message)
        {
            this.Message = message;
            return this;
        }

        public Builder CreatedOn(DateTime dateTime)
        {
            this.DateTime = dateTime;
            return this;
        }

        public Builder OnUrl(string url)
        {
            this.Url = url;
            return this;
        }

        public static implicit operator LogEntry(Builder builder)
        {
            return new LogEntry(builder);
        }
    }
}

A criação de uma nova entrada passou de:

LogEntry entry = new LogEntry(LogLevel.Error, "System is out of memory!", DateTime.Now, "http://localhost/ListOnlineUsers.aspx");

Para:

LogEntry entry = new LogEntry.Builder()
                                .AsError()
                                .MessageIs("System is out of memory!")
                                .CreatedOn(DateTime.Now)
                                .OnUrl("http://localhost/ListOnlineUsers.aspx");

A maior vantagem desta nova solução é a facilidade na leitura do código. No primeiro caso é mais difícil saber o que cada parâmetro significa, é necessário ver o nome de cara parâmetro no construtor da classe. Além disso, o primeiro exemplo está nos obrigando a atribuir as quatro propriedades, se uma delas fosse opcional, teríamos que escrever um novo construtor (aka Telescopic Constructor Pattern). Novamente a tendência é criarmos vários construtores conforme a necessidade. No segundo caso é possível atribuir apenas as propriedades que sejam necessárias.

Percebam também que temos a opção de definir o tipo de log utilizando os método auxiliares AsError(), AsWarning() e AsInformation() ou então, caso necessário, ainda podemos utilizar o WithLevel(LogLevel). Fica bastante agradável de ler e escrever, não acham?

Em Java seria necessário criar um método build() em nosso LogEntry.Builder que iria ser responsável por criar e devolver uma instância do LogEntry. No .Net é possível substituirmos este método por um operador de conversão implícita e evitar ter que explicitamente pedir para o builder construir o objeto.

Gostou? Espero que façam um bom uso :)

Não gostou de algo ou achou algum problema? Compartilhe!

Ps1.: Para quem nunca achou uma utilidade para as classes internas do .Net/Java, eis aqui um bom exemplo.
Ps2.: O código está no GitHub.
Ps3.: Nó GitHub eu dividi a classe em em dois arquivos utilizando o recurso de classes parciais, achei que ficou mais organizado.

Controlando a crição dos Controllers no ASP.NET MVC

Uma das características que eu mais gosto do ASP.NET MVC é sua extensibilidade, existem diversos pontos que são possíveis de customizar e configurar para que o framework se comporte da maneira que você quer.

A criação dos controllers é um destes pontos e possivelmente um dos menos usados pelos desenvolvedores por ai. Talvez por desconhecimento ou não saber como tirar proveito disso, não sei. Foi isso que me levou a criar este post onde vou apresentar o que podemos fazer ao tomar controle sobre a construção dos controllers.

Antes de prosseguirmos, sabiam que os Controllers utilizam o princípio de Hollywood? Este princípio se resume na seguinte frase “não nos chame, nós chamaremos você” (don’t call us, we’ll call you). Isso significa que, ao criar um novo Controller, você não precisa dizer ao ASP.NET MVC quando chamar ou então como criá-lo, você apenas o define e deixa que o próprio framework cuide do ciclo de vida  dele.

Voltando ao assunto principal, quando realizamos um HTTP Request o framework utiliza as definições de rotas para descobrir qual o controller que deve ser criado e então repassa a responsabilidade de criação para a classe DefaultControllerFactory.

Os controllers são objetos como qualquer outro e é da natureza da Programação Orientada a Objetos que haja a “troca de mensagens” entre os objetos e consequentemente uma dependência de um objeto para outro. É comum um controller depender de uma classe de serviço ou de acesso à dados, a maneira mais comum de gerenciarmos isso é através dos contrutores. Um controller com dependência ficaria assim:

private IMailService mailService;

public HomeController(IMailService mailService)
{
    this.mailService = mailService;
}

Se executarmos assim como está, recebemos uma exception com a mensagem “No parameterless constructor defined for this object.“. Se olharmos um pouco o código do DefaultControllerFactory é possível perceber que é utilizado o Activator.CreateInstance sem a passagem de parâmetros, isso significa que ele irá procurar um construtor sem parâmetros. No nosso caso onde não podemos aceitar a construção do HomeController sem o serviço de e-mail, a solução é tomar controle sobre a criação dos controllers e customizá-los como bem quisermos. Uma implementação bem simples ficaria assim.

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == typeof(HomeController))
            return new HomeController(new LocalMailService());
        return base.GetControllerInstance(requestContext, controllerType);
    }
}

Apesar de eu ter herdado de DefaultControllerFactory, seria perfeitamente aceitável implementar diretamente a IControllerFactory, a diferença é que assim eu economizo diversas linhas de código já que não preciso escrever todos os métodos exigidos pela interface. Para registrar esta customização, é necessário adicionar a seguinte instrução no Application_Start do Global.asax.

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

Done! Agora nosso controller está sendo criado com uma instância de LocalMailService. Isso é chamado de injeção de dependência via construtor. Como eu disse acima, essa é uma implementação simples, se olharmos com mais cuidado a classe CustomControllerFactory é possível verificar que esta solução não escala. Se para cada controller for necessário adicionar uma nova condição, nossa classe vai crescer em tamanho e complexidade.

A solução mais utilizada e até onde eu sei a mais recomendada, é utilizar um framework de injeção de dependência. Neste exemplo vou utilizar o StructureMap, o download está disponível no site oficial.

Ao utilizar o StructureMap, a solução final fica assim.

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterDependencies();

    ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

private void RegisterDependencies()
{
    ObjectFactory.Configure(x =>
        x.For<IMailService>().Use<LocalMailService>()
    );
}

E nossa ControllerFactory ficou bem mais limpa.

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    return ObjectFactory.GetInstance(controllerType) as IController;
}

Quando tivermos novos controllers e novas dependências basta registar no StructureMap e o resto já estará sendo feito sozinho.

UPDATE: Para quem está usando o MVC3 e está buscando injeção de dependência nos controllers, uma outra opção ainda mais elegante é usar o DependencyResolver,  o Vínicius Quaiato fez um post sobre isso.

Espero que seja útil :)

Dica #6: Escrevendo testes fluentes


Aqui vai uma dica rápida. Quando comecei a escrever meus primeiros testes automatizados eu tinha algo assim.

int actual = calculadora.Somar(1 +2);
int expected = 3;
Assert.AreEquals(expected, actual);

Após um tempo, comecei a ganhar mais experiência e aprendi algo chamado de Fluent Interfaces. O objetivo desta “técnica” é escrever código que seja fácil de ler. O código acima é fácil, correto? Mas da para melhor, quer ver?

Veja este exemplo aqui.

int actual = calculadora.Somar(1 +2);
Assert.That(actual, Is.EqualTo(3));

Melhorou, não acha? Legibilidade faz parte do Clean Code e Fluent Interfaces é uma excelente técnica que pode te ajudar a deixar seu código mais fácil de ler. Reserve um tempo e procure entender melhor como funciona. Além disso, a partir do C# 3.0 tivemos a adição das Closures que também é outro recurso poderosísimo na busca pela Legibilidade.

Nota: Estes exemplos acima foram construidos com NUnit e já vem pronto, não sendo necessário adicionar dependência para nenhuma outra biblioteca. No caso do MSUnit, eu recomendo o Sharp Tests Ex, que alías também funciona com o NUnit.

É isso ai e até a próxima.

Um código limpo diz mais que mil comentários

Você comenta seu código? Sim? Por quê?

Cuidado! Comentários podem ser perigosos! O uso deles pode ser um indício de que seu código está confuso e você tem medo de precisar alterar algo mais tarde e esquecer o que aquela variável aux armazena.

A forma mais elegante de se remover comentários é refatorando. Aux é um nome sem sentido, você pode até lembrar o que essa variável significa enquanto você ainda está programando, mas daqui duas semanas você já vai ter esquecido. Nomeie melhor suas variáveis, não tenha medo de ter uma variável assim: listaDeClientesInativos ao invés de lista.

Outro caso bastante comum de comentários é quando você tem aquele método gigante com 100 linhas e a cada 10 você resolve colocar um comentário para dizer o que está acontecendo. Nestes casos você pode (e deve) utilizar extract method. Divida seu grande método em outros pequenos, assim você pode substituir os comentários por métodos com nome auto-explicativos, além disso estará de acordo com o Single responsibility principle.

Não é possível garantir que outras pessoas do seu time irão atualizar o comentário quando alguma alteração for feita. Pior do que ter um método cheio de comentário é ter um método com um comentário obsoleto. Além disso, a leitura do comentário é opcional, você não tem como saber se seu comentário será lido ou não. Se você tem algo importante para dizer, diga no seu código, através de nomes bem definidos e código limpo.

Dito tudo isso, não preciso nem comentar sobre o exemplo abaixo né?

//Chamado #213: Corrigido bug onde o desconto final estava sendo somado ao invés de descontar.
int total = pedido.getTotal() - descontoFinal;

Se você faz isso, sugiro procurar algum software de controle de versão.

Algum tempo atrás escrevi sobre Expressões Regulares (aqui) e falei sobre comentários em Regex pattern. Continuo achando uma forma elegante de se utilizar comentários já que um pattern quase nunca é limpo, é sempre complicado ler aquilo e entender. Os comentários ajudam neste caso tornando-o mais legível. Outra opção seria escrever testes automatizados para seu Regex. Além dos testes servirem como exemplos de uso, você estará tendo uma maior cobertura do seu código e a garantia de poder alterar o pattern sem medo de inserir um novo bug.

Falando em Regex e Testes, lembrei deste tweet do @gchapiewski.

It’s impossible to program complex regular expressions without TDD. Period.

Resumindo. Mais código limpo e menos comentários. Ah, não esqueça de ler o livro Clean Code. :)

Evitando o excesso de ‘null check’ (continuação)

Conforme comentado no outro post, vou descrever aqui outra forma de evitar a verificação de um objeto nulo. Este tipo de verificação que é tão usada causa um bad smell tremendo.

Imagine a seguinte funcionalidade de um sistema de vendas e-commerce:

Quando nós enviarmos um pedido para um cliente que comprou em nossa loja, nós queremos que este cliente seja avisado que seu produto já foi deixado nos correios. Gostariamos de enviar um SMS para os clientes que consideramos ótimo e um e-mail para aqueles que achamos que são bons clientes. Qualquer outro cliente não deve receber o aviso, se ele tiver interesse ele passa no site e verifica o status.

Então, modelando esta user story, podemos ter uma interface chamada de Notificador e algumas implementações como NotificadorViaSms e NotificadorViaEmail. Isso nos permite que em um dado momento possamos incluir algum outro tipo de notificador, por exemplo, NotificadorViaTwitter.

Pois bem, tendo múltiplas implementações eu vou precisar de um factory que vai ser responsável por criar um notificador baseado no tipo do cliente. Aqui vai um exemplo do código:

public class NotificadorFactory {
	public static Notificador cria(Pedido pedido) {
		if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
			return new NotificadorPorSms(pedido);
		else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
			return new NotificadorPorEmail(pedido);
		else
			return null;
	}
}

Seu uso ficaria assim:

Cliente maria = new Cliente("Maria", TipoCliente.Otimo);
Pedido pedido2 = new Pedido(maria);
pedido2.setTotal(520.10f);

Notificador notificador = NotificadorFactory.cria(pedido2);
notificador.enviar();

Então, o que acharam? Ficou legal, não? Eu gostei :) (só não gostei do nome da factory e do seu método, mas não é esse o objetivo).

Só que este código tem um problema. E se a cliente “maria” fosse do tipo Ruim ao invés de Otimo? Segundo a minha user story, eu não deveria avisar ela. Minha factory está correta, se o cliente for do tipo Ruim ela não vai retornar nenhum Notificador, vai retornar null. Quando meu código cliente for chamar o método enviar() vai surgir um NullPointerException.

Solução: coloca um “if notificador != null” antes de chamar o método. Yeah! Resolvemos o problema, mas criamos outro… Em todo lugar que você for usar o NotificadorFactory você vai ter que se lembrar de que ele pode retornar um valor nulo, e por isso será necessário verificar se o retorno é nulo na maioria das chamadas.

Outra solução seria aplicar o Null Object Pattern. Segundo a definição encontrada no Wikipedia:

Original:

In object-oriented computer programming, a Null Object is an object with defined neutral (“null”) behavior. The Null Object design pattern describes the uses of such objects and their behavior (or lack thereof).

Traduzido:

Em programação orientada a objetos, um Objeto Nulo é um objeto que possui comportamento neutro (nulo). O padrão de projeto Null Object descreve o uso de tais objetos e seu comportamento (ou a falta dela).

De forma simplificada, o Objeto Nulo segundo o padrão de projeto é um objeto que não faz nada, que possui um comportamento neutro. Em nosso caso poderiamos ter um notificador chamado NotificadorVazio que seria criado pelo NotificadorFactory ao invés de retornar null. Sendo assim, o código cliente ficaria livre para utilizar o retorno da factory sem se preocupar com NullPointerException pois o retorno nunca seria nulo. Curioso para saber como fica a implementação do NotificadorVazio? Olha que simples:

A Interface:

public interface Notificador {
	void enviar();
}

A implementação nula:

public class NotificadorVazio implements Notificador {
	@Override
	public void enviar() {
		//Não fazer nada
	}
}

A factory:

public class NotificadorFactory {
	public static Notificador cria(Pedido pedido) {
		if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
			return new NotificadorPorSms(pedido);
		else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
			return new NotificadorPorEmail(pedido);
		else
			return new NotificadorVazio();
	}
}

Pronto, resolvemos o problema de forma organizada, orientada a objetos e utilizando padrões bem definidos.

Até mais :D

Evitando o excesso de ‘null check’

Que atire uma pedra aquele que ainda não está cansado de ver/escrever código assim:

public Departamento insere(Integer codigo, String descricao) {
    if (descricao == null) || (descricao.equals("")) {
        //faz alguma coisa
    }
}

Poxa, é impossível. Cada desenvolvedor deve escrever isso no mínimo umas 10 vezes por dia. Se você faz isso todo dia, acha um saco e nunca achou uma maneira para resolver esse ‘problema’, eis a solução:

public Departamento insere(Integer codigo, String descricao) {
    //faz alguma coisa
}

Simples não? Problema resolvido.

A documentação deve deixar bem claro que este método não aceita nulo ou vazio no parâmetro ‘descricao’.

NOOOOOOOO! Null Pointer Exception.

NOOOOOOOO! Null Pointer Exception.

Se este método for interno à sua aplicação, fica fácil, você sabe que as classes que irão chamar este método precisam estar escritas de tal maneira que não passe nenhum valor nulo ou vazio, caso contrário, haverá algumas excessões não esperadas.
Se você prestar atenção, ao remover essas verificações de toda a hierarquia de classes, as únicas classes que realmente irão precisar da verificação são as classes que interajem com a interface do usuário.

Agora se seu método estiver exposto em um WebService, isso significa que outros desenvolvedores poderão chamar este método e você não tem controle sobre o que estes desenvolvedores irão passar para o método. Neste caso, ainda assim, deixe o método sem as verificações e faça-as nas interfaces do WebService. Crie pré-condições para seus serviços, deixe claro que o parâmetro descrição deve conter algum valor. Se a condição estiver válida, você pode chamar o método tendo a garantia de que não haverá excessões.

É comum que quando nos deparamos com um erro de NullReferenceException (C#) ou NullPointerException (Java) no log da aplicação, a primeira coisa que fazemos é ir direto na linha que deu erro e adicionar a verificação se é nula apenas para que não ocorra tais erros. Se um valor nulo chegou até ali é porque ele deveria ter parado em algum lugar antes, geralmente na camada entre a interface do cliente e o código de negócio.

Lembre-se do princípio da responsabilidade exclusiva! Não é responsabilidade do método ‘insere’ garantir que os parâmetros estejam válidos.

Além deste caso onde você pode ter problemas com parâmetros nulo, existem lugares em que você faz a verificação de nulo com o retorno de um método, ficando mais ou menos assim:

Departamento depto = dao.insere(1, "Compras");
if (depto != null) {
    //Faz algo
}

E nesses caso, como se comportar? Isso vai ficar para o próximo post, lá eu falo sobre isso e um pattern relacionado, chamado de Null Object Pattern. Até mais!

Edit #1: O post sobre Null Object Pattern é este aqui.

Conselho do Tio Bob

Esse vídeo foi gravado já faz um tempo pelo Fabio Akita e foi postado no blog dele. Eu acho este vídeo tão bom que resolvi colocá-lo aqui, talvez alguém ainda não tenha visto.

100% de Code Coverage?

De forma simplificada,  o code coverage é uma métrica usada em testes automatizados que aponta o percentual de linhas de código que estão sendo cobertas pela suite de teste. Ter 100% de coverage significa que toda linha do seu código de produção está sendo executada pelo menos uma vez.

Sabendo disto, lhe pergunto, o que você ganha ao atingir 100%?

Ter um software com 100% de cobertura de testes não significa que seu software é bug-free e muito menos que ele é melhor que um software que possui apenas 80%. Além disso, eu poderia até dizer que 100% de cobertura é quase impossível. Nos projetos em que eu tentei cobertura total, geralmente eu conseguia no máximo 95% usando testes um tanto quanto inúteis, apenas para aumentar o percentual. O maior problema ao estabelecer uma meta de 100% é que seu foco ao escrever um teste acaba se voltando para atender esta meta, e não mais para testar um requisito.

Um adepto de TDD geralmente terá perto de 90% de coverage. Porquê? Porque se você escreve o teste antes, ao codificar o código de produção você fará apenas o necessário para o teste passar, nem uma linha de código a mais, nem a menos, ou seja, 100%. Em todo caso, por algum motivo de seu framework ou linguagem, você é obrigado a escrever algumas linhas não esperadas no seu teste, e isso vai fazer seu coverage baixar, por isso o 90%.

Eu não tenho o costume de verificar o coverage constantemente, executo apenas uma vez ou outra para ter uma idéia de como está a situação. Se está muito baixo, e eu considero baixo até 80%, eu busco encontrar o que está baixando tanto assim minha coberturda. As vezes é porque um método que foi criado e que não é mais utilizado ainda está dentro de uma classe, ou então uma classe que por algum motivo eu esqueci de testar. Enfim, para mim, 80% ~ 100% é um valor aceitável.

Para quem nunca usou code coverage em C#, eu recomendo baixar o plugin TestDriven.Net que já vem com diversos frameworks de teste como MbUnit e NUnit além do NCover.

Veja abaixo uma imagem das informações mostradas pelo NCover. Aquele percentual vermelho ali mostra que apenas X% das linhas daquela classe estão sendo executadas no teste. Por exemplo a classe CodeUtil (0%), não possui teste nenhum.

Dica #4: Minhas recomendações ao usar Expressões Regulares

Foi lançado recentemente a terceira edição do livro “Expressões Regulares – Uma abordagem divertida” do autor Aurélio Marinho Jargas. Tive a oportunidade de conhecê-lo pessoalmente no lançamento e ainda ganhar um livro gratuito por ter sido sorteado na promoção do Twitter, aliás, é a primeira vez que eu ganho algo no Twitter :)

Sobre o Livro

Expressões Regulares - Uma abordagem Divertida

#piazinho3

Eu já havia lido a edição anterior, e esta nova edição ficou bem parecida, continua seguindo o que o título propõe: Ser uma abordagem divertida. Você consegue ler o core dele em uma viajem de avião ou em algumas corridas de ônibus de ida e volta da faculdade. Eu defino o core do livro como sendo aquilo que você usa independente do lugar, linguagem ou ferramenta. Seriam os primeiros capítulos onde ele fala dos metacaracteres e suas combinações.

Achei bem bacana a novidade que ele traz no início do livro, pelo menos eu não lembro ter lido isso na edição anterior. O Aurélio conta como foi sua trajetória durante todas as publicação de seus livros. Cara, como um programador/pesquisador vira fotógrafo de surfe? Inacreditável, seria uma das últimas profissões que passaria em minha mente.

Confesso que depois de ler deu vontade de escrever um livro, quem sabe não sai algo nos próximos anos?

Bom, meu objetivo com o post não é falar do livro em si, e sim de boas práticas com Expressões Regulares. Não irei explicar o que é, como funciona ou como fazer uma ER para casar com isso ou aquilo. Vou considerar que você já tenha uma noção, até mesmo básica, de Expressões Regulares. Não sabe? Compre o livro :)

Práticas que recomendo com Expressões Regulares

Enumerei quatro itens que acho pertinente ao assunto, que são os seguintes.

1) Violação do princípio da responsabilidade exclusiva (SRP)

É preciso tomar cuidado com o local onde você coloca os casamentos de suas expressões regulares. Se for inserir a expressão dentro de um método cuja a responsabilidade não seja validar a entrada com base na expressão, você estará atropelando o Single Responsibility Principle, defendido pelo Guru Uncle Bob (mais detalhes sobre o SRP você encontra aqui).

Validar a entrada contra uma expressão regular já uma responsabilidade e não deve ser misturada com outras, separe as responsabilidades em classes distintas, facilita a manutenção, o design estará de acordo com o SRP e ajudará no item 2 desta lista.

2) Comentários de exemplos

Sou contra o uso de comentários, sou da opinião de que se você precisa comentar é porque está dificil de entender. Ora, se está dificil de entender, arrume e não comente. Nada pior do que ver 5 linhas de código e 20 de comentário. Se estiver com dificuldades para tornar o código mais limpo, sugiro que dê uma olhada no livro Clean Code do Uncle Bob.

Depois de expressar meu motivo pela qual sou contra o uso de comentários, preciso dizer que, como tudo na vida, existem exceções. Nas Expressões Regulares por exemplo, é muito útil informar com o que aquele pattern vai casar, com e-mails? sites? telefones?

Colocar exemplos de entradas que casam e que não casam também ajuda bastante na leitura. Veja como ficaria um exemplo em C#.

// Pattern para verificar o formato de e-mail.
//
// Exemplos válidos
// - joazinho@piadas.com.br, maria@algumsite.com.br
// Exemplos inválidos
// - joazinhopiadas.com.br, maria@algumsite
string emailPattern = @"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$";

3) Quanto mais legível, melhor

Esta dica está no livro, tamanho não é documento, cuidado com expressões gigantes e com aquelas minúsculas, procure sempre fazer um pattern legível.

As vezes é difícil deixar essa sopa de letrinhas de modo organizado, um exemplo legal é este do e-mail logo acima. Você consegue entender? É difícil né? Ainda bem que há comentários.

4) Procure alternativas

Tenha certeza que existem N maneiras de resolver um problema, expressões regulares são ótimas mas não são as balas de prata. Não complique, conheça seu framework, procure a melhor solução. Para validação no input de dados preciso concordar que as expressão resolvem com bastante facilidade, mas para descobrir se um caracter é um número ou não, em C# você pode usar o char.IsDigit(). Muito mais elegante, concorda?

Se você não possui um equivalente em sua linguagem preferida, crie. Mas não esqueça de separar ela em outra classe, comentar e deixar legível :)

http://butunclebob.com/ArticleS.UncleBob

A teoria da janela quebrada e o desenvolvimento de software

Teoria da Janela Quebrada

Teoria da Janela Quebrada

EDIT: Este post foi criado dia 22/10/2009 e alterado dia 31/01/2010.

A teoria da janela quebrada foi formulada em Nova Iorque após algumas experiências feitas em bairros nobres da cidade. Em um dos testes realizados, um carro foi deixado  parado em uma rua e durante uma semana inteira ninguém tocou no carro. Os responsáveis pela experiência então quebraram propositalmente uma das janelas do carro e deixaram por mais uma semana exatamente no mesmo lugar. Qual foi o resultado? O carro foi completamente destruído, janelas quebradas, peças roubadas dentre outras degradações.

Esta teoria é aplicada em uma Política de Tolerância Zero nos estados unidos onde cada crime, por mais pequeno que seja, passa por uma punição. Uma simples pixação já é suficiente para que o delinquente seja levado pela polícia e a parede pixada é rapidamente limpada. Com o tempo percebeu-se que havia diminuido a quantidade de pixação já que era sabido que havia punição e que a polícia estava sempre de olho. Esta teoria foi assunto de um livro chamado Fixing Broken Windows: Restoring Order And Reducing Crime In Our Communities sobre a sua utilização no combate à crimanilidade.

Vejo que isto também acontece bastante com os aparelhos eletrônicos. Nos primeiros dias após a compra está tudo em perfeito estado, todo o cuidado é pouco para não danificá-lo. Todo esse cuidado acaba quando ele cai pela primeira vez. Na hora da até um aperto no coração, porém, a partir da segunda ou terceira queda ninguém se importa mais.

O que isso tem a ver com desenvolvimento de software? Tudo. O primeiro dia de um projeto é o paraíso, existem poucas classes no sistema, complexidade do código está baixa e o prazo de entrega está bem longe. O tempo vai passando, a complexidade aumentando e o prazo apertando, a tendência é que a qualidade do código baixe, pois você precisa se preocupar mais com a entrega do que com o design do código. Você adquire o famoso Technical Debt. Nesses momentos surge frases como: “Cara, faz assim mesmo, depois da entrega do produto eu te dou mais uma semana para arrumar isso”. Esse dia nunca chega.

Tendo um produto com design ruim ninguém vai se importar de fazer uma manutenção e continuar errando, o que vai contra a regra dos escoteiros, que diz: “Deixe o acampamento mais limpo do que você encontrou” e também se aplica à software.

O que fazer nestes casos? Refactore, refatore e refatore um pouco mais. Deixe o código o mais limpo possível. Se as donas de casa limpam TODA a casa uma vez (ou duas) por semana, porque também não fazemos o mesmo com nosso software?

E ai, você atua em um projeto com janelas quebradas? Cuidado.

Sharing Buttons by Linksku