Tag Archives: design pattern

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.

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

Sharing Buttons by Linksku