domingo, 2 de fevereiro de 2014

Composite Pattern: Um exemplo prático e usável

O padrão de projeto Composto -- conhecido como Composite Pattern -- é um padrão em que um cliente lida com um outro objeto o tratando como apenas um. E este único objeto geralmente possui uma coleção de objetos de um mesmo tipo.

É mais fácil demonstrar um exemplo prático do que explicar a teoria deste padrão. Vamos supor um cenário (que eu mesmo já enfrentei algumas vezes) para lidar com tratamento de erros de um sistema.

Imagine que queremos logar todos os erros de um sistema num arquivo de texto e, dependendo da criticidade do erro, enviar um email ao administrador do sistema. Com este cenário, conseguimos extrair 2 objetos (logar em texto e enviar email) que possuem um denominador em comum:

LidarComUmErro

Com o padrão Composite, conseguimos que os clientes de um componente lidem com uma coleção como se estivessem lidando com apenas um objeto. Dessa forma, poderíamos criar dois tipos de objetos que compartilham uma ideia parecida (LidarComUmErro):


public class LogArquivoTexto
{
 public void LogarErro(object erro)
 {
  // lógica para salvar os detalhes do erro em um arquivo de texto
 }
}


public class NotificadorDeErroCritico
{
 public void NotificarErroCritico(object erro)
 {
  // lógica para enviar a notificação de erro crítico por email
 }
}


Se usarmos a beleza da abstração, conseguiríamos uma interface como a seguir:

public interface GerenciadorDeErros
{
 void GerenciarErro(object erro);
}

Nós abstraimos a ideia geral dos componentes que logam um erro num arquivo e enviam um email sobre um erro, correto?

Com algumas modificações, conseguiríamos que os dois componentes realizassem a interface de GerenciadorDeErros:


public class LogArquivoTexto
 : GerenciadorDeErros
{
 public void GerenciarErro(object erro)
 {
  LogarErro(erro);
 }
 
 public void LogarErro(object erro)
 {
  // lógica para salvar os detalhes do erro em um arquivo de texto
 }
}


public class NotificadorDeErroCritico
 : GerenciadorDeErros
{
 public void GerenciarErro(object erro)
 {
  LogarErro(erro);
 }
 
 public void NotificarErroCritico(object erro)
 {
  // lógica para enviar a notificação de erro crítico por email
 }
}


O que temos até agora  é a base para usarmos o padrão Composite. Precisamos de um cara que nos permita trabalhar com nossos dois componentes como se fossem um só:


public class GerenciadorDeErrosDoMeuSistema
 : GerenciadorDeErros
{
 public void GerenciarErro(object erro)
 {
  // O que fazemos agora?!?
 }
}

Sem mais delongas, a implementação que nos permitiria trabalhar com uma coleção parecendo que é apenas um objeto seria como a seguir:


public class GerenciadorDeErrosDoMeuSistema
 : GerenciadorDeErros
{
 private IList<GerenciadorDeErros> _gerenciadoresDeErros;
 
 public GerenciadorDeErrosDoMeuSistema()
 {
  _gerenciadoresDeErros = new List<GerenciadorDeErros>();
  _gerenciadoresDeErros.Add(new LogArquivoTexto());
  _gerenciadoresDeErros.Add(new NotificadorDeErroCritico());
 }
 
 public void GerenciarErro(object erro)
 {
  foreach(GerenciadorDeErros gerenciadorDeErro in _gerenciadoresDeErros)
  {
   gerenciadorDeErro.GerenciarErro(erro);
  }
 }
}


E este objeto seria usado mais ou menos como a ideia seguinte:

try
{
 ...
}
catch(Exception ex)
{
 GerenciadorDeErros gerenciadorDeErros = AlgumaFormaParaObterOGerenciadorDeErros();//
 gerenciadorDeErros.GerenciarErro(ex);
}

Pronto!

Espero ter passado de forma clara um exemplo prático e usável deste padrão.

Nenhum comentário:

Postar um comentário