Mão na massa: Chain of Responsibility

Problema:

Uma aplicação de e-commerce precisa se comunicar com vários bancos diferentes para prover aos seus usuários mais possibilidades de pagamentos, atingindo assim um número maior de usuários e facilitando suas vidas.

Ao modelar uma forma de execução do pagamento, dado que precisamos selecionar um entre vários tipos de bancos, a primeira ideia que surge é utilizar uma estrutura de decisão para verificar, dado um parâmetro, qual o banco correto deve ser utilizado.

já discutimos no post sobre o padrão Strategy que utilizar uma estrutura de decisão pode ser muito complexo, e vimos uma boa solução para este problema. Também vimos exemplos de utilização de estruturas de decisão nos padrõs Abstract Factory e Factory Method.

Poderíamos utilizar os métodos fábricas para gerar o objeto correto para ser utilizado na nossa aplicação. Poderíamos criar estratégias diferentes para cada banco e escolher em tempo de execução.

Em todas estas soluções, nós utilizamos uma forma de “esconder” a estrutura de decisão por trás de uma interface, ou algo similar, para que as alterações fossem menos dolorosas. No entanto, continuamos utilizando as estruturas de decisão.

Vamos analisar então o padrão Chain of Responsibility, que promete acabar com estas estruturas.

Chain of Responsibility

O padrão Chain of Responsibility possui a seguinte intenção:

“Evitar o acoplamento do remetente de uma solicitação ao seu receptor, ao dar a mais de um objeto a oportunidade de tratar a solicitação. Encadear os objetos receptores, passando a solicitação ao longo da cadeia até que um objeto a trate.” [1]

Pela intenção percebemos como o Chain of Responsibility acaba com as estruturas de decisão, ele cria uma cadeia de objetos e vai passando a responsabilidade entre eles até que alguém possa responder pela chamada.

Vamos então iniciar construindo uma pequena enumeração para identificar os bancos utilizados no nosso sistema:

public enum IDBancos {
	bancoA, bancoB, bancoC, bancoD 
}

Agora vamos construir a classe que vai implementar a cadeia de responsabilidades. Vamos exibir partes do código para facilitar o entendimento.

public abstract class BancoChain {

	protected BancoChain next;
	protected IDBancos identificadorDoBanco;

	public BancoChain(IDBancos id) {
		next = null;
		identificadorDoBanco = id;
	}

	public void setNext(BancoChain forma) {
		if (next == null) {
			next = forma;
		} else {
			next.setNext(forma);
		}
	}
}

A nossa classe possui apenas dois atributos, o identificador do banco e uma referência para o próximo objeto da corrente. No construtor inicializamos estes atributos. O método setNext recebe uma nova instância da classe e faz o seguinte:

Se o próximo for nulo, então o próximo na corrente será o parâmetro. Caso contrário, repassa esta responsabilidade para o próximo elemento. Assim, a instância que deve ser adicionada na corrente irá percorrer os elementos até chegar no último elemento.

O próximo passo será criar o método para efetuar o pagamento.

	public void efetuarPagamento(IDBancos id) throws Exception {
		if (podeEfetuarPagamento(id)) {
			efetuaPagamento();
		} else {
			if (next == null) {
				throw new Exception("banco não cadastrado");
			}
			next.efetuarPagamento(id);
		}
	}

	private boolean podeEfetuarPagamento(IDBancos id) {
		if (identificadorDoBanco == id) {
			return true;
		}
		return false;
	}

	protected abstract void efetuaPagamento();

A primeira parte do algoritmo de pagamento é verificar se o banco atual pode fazer o pagamento. Para isto é utilizado o identificador do banco, que é comparado com o identificador passado por parâmetro. Se o elemento atual puder responder a requisição é chamado o método que vai efetuar o pagamento de fato. Este método é abstrato, e as subclasses devem implementá-lo, com seu próprio mecanismo.

Se o elemento atual não puder responder, ele repassa a chamado ao próximo elemento da lista. Antes disto é feita uma verificação, por questões de segurança, se este próximo elemento realmente existe. Caso nenhum elemento possa responder, é disparada uma exceção.

Agora que definimos a estrutura da cadeia de responsabilidades, vamos implementar um banco concreto, que responde a uma chamada.

public class BancoA extends BancoChain {

	public BancoA() {
		super(IDBancos.bancoA);
	}

	@Override
	protected void efetuaPagamento() {
		System.out.println("Pagamento efetuado no banco A");
	}
}

O Banco A inicializa seu ID e, no método de efetuar o pagamento, exibe no terminal que o pagamento foi efetuado. A implementação dos outros bancos segue este exemplo. O ID é iniciado e o método de efetuar o pagamento exibe a saída no terminal.

O cliente deste código seria algo do tipo:

public static void main(String[] args) {
	BancoChain bancos = new BancoA();
	bancos.setNext(new BancoB());
	bancos.setNext(new BancoC());
	bancos.setNext(new BancoD());
	
	try {
		bancos.efetuarPagamento(IDBancos.bancoC);
		bancos.efetuarPagamento(IDBancos.bancoD);
		bancos.efetuarPagamento(IDBancos.bancoA);
		bancos.efetuarPagamento(IDBancos.bancoB);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

O diagrama UML deste exemplo seria:

Um pouco de teoria

Vimos pelo exemplo que o padrão Chain of Responsibility fornece uma maneira de tomar decisões com um fraco acoplamento. Perceba que a estrutura de cadeia não possui qualquer informação sobre as classes que compõem a cadeia, da mesma forma, uma classe da cadeia não tem nenhuma noção sobre o formato da estrutura ou sobre elementos nela inseridos.

Assim, é possível variar praticamente todos os componentes sem grandes danos ao projeto. Cada elemento implementa sua própria maneira de responder a requisição, e estas podem ser alteradas facilmente.

O problema é que é preciso tomar cuidado para garantir que as chamadas sejam realmente respondidas. No exemplo foi feita uma verificação para saber se o próximo elemento é nulo, para evitar uma acesso ilegal. Mas esta é uma solução para este problema específico. Cada problema exige o seu próprio cuidado.

Código fonte completo

O código completo pode ser baixado no seguinte repositório Git: https://github.com/MarcosX/Padr-es-de-Projeto.

Os arquivos estão como um projeto do eclipse, então basta colocar no seu Workspace e fazer o import.

Se gostou do post compartilhe com seus amigos e colegas, senão, comente o que pode ser melhorado. Encontrou algum erro no código? Comente também. Possui alguma outra opinião ou alguma informação adicional? Comenta ai! 😀

Referências:

[1] GAMMA, Erich et al. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos.

Anúncios

9 comentários sobre “Mão na massa: Chain of Responsibility

  1. Muito bom artigo, Brizeno, parabéns!
    Uma observação meio fora do assunto, faça uma revisão ortográfica na seção “about.me”. Dará mais credibilidade ao seu blog.
    Obrigado!

  2. Muito bom, muito obrigado por compartilhar o conhecimento!

  3. Ótimo trabalho me ajudou bastante.

  4. Cara foi uma linguagem simples com exemplos simples que descreve exatamente seu uso, parabéns!

  5. Boa tarde Marcos, no seu post sobre Chain of Responsibility não consegui perceber a vantagem em utilizar ele no lugar do Strategy, pois você não executa toda a cadeia de comandos, só executa o comando que atenda ao parâmetro passado!

    Se poder por favor esclarece essa minha dúvida.

    Desde Obrigado

  6. Boa tarde, não conseguir perceber a vantagem de usar no Chain of Responsibility ao invés do Strategy, pois em ambos você verifica o parâmetro passado, não seria mais apropriado utilizar o Strategy?
    Obrigado pelos post’s 😀

Deixe um comentário

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

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s