O Padrão Simple Factory

Leia mais sobre Padrões de Projeto no livro Refatorando com Padrões de Projeto e no ebook gratuito Primeiros passos com Padrões de Projeto

Criar objetos geralmente é uma tarefa simples, passamos valores para o construtor e pronto, temos um objeto instanciado e pronto para uso. No entanto, algumas vezes a criação não é tão direta assim e alguma manipulação com os dados pode ser necessária. Em outros casos é preciso configurar o objeto, mesmo depois da execução do construtor.

Quando instanciar objetos não for uma tarefa tão simples, os padrões de criação oferecem boas soluções. Como vimos antes, os padrões documentados pela Gangue dos Quatro foram classificados em grupos de acordo com o tipo de problema que resolvem. Padrões que facilitam criar novas instâncias de objetos são classificados como padrões de criação.

Uma versão simples e que pode ser utilizada mais facilmente, é o Simple Factory. Nele vamos extrair a lógica que cria objeto para uma classe especializada, deixando que a manipulação dos dados não atrapalhe o restante da lógica de negócio. Alguns autores nem consideram o Simple Factory como um padrão, devido a sua simplicidade.

Exemplo de Aplicação

Na Listagem 1 temos um exemplo de código que pode se beneficiar do uso do Simple Factory. Nele, o método buscarProdutosPreferenciais recebe um usuário consulta um serviço externo para obter os produtos que o usuário costuma comprar. Ao final, os produtos são validados e filtrados para que apenas aqueles com estoque sejam retornados.

No entanto, antes de chegar na lógica que realmente faz a busca dos produtos preferenciais do usuário, é preciso configurar a chamada ao serviço externo. Essa responsabilidade extra aumenta o tamanho e a complexidade do método.

public List<Produto> buscarProdutosPreferenciais(Usuario usuario) {
  ConfiguracoesServicos config = new ConfiguracoesServicoProdutosPreferenciais();
  config.setRecurso(“/produtos/preferenciais/”);
  config.setIdUsuario(usuario.getId());

  if (getAmbienteAtual() == Ambiente.LOCAL) {
    config.setEndpoint(“localhost:1234”);
    config.setXid(gerarUuid());
    config.setVersaoApi(“2.1”);
  } else {
    if (toggleApiNova.habilitado()) {
      config.setVersaoApi(“2.1”);
      config.setXid(gerarUuid());
    } else {
      config.setVersaoApi(“1.9”);
      config.setXid(“”);
    }
    config.setEndpoint(“https://intra.” + getAmbienteAtual().toString() + “.mega\
corp.com/”);
  }

  ServicoRest servico = new ServicoRest(config);
  List<Produto> produtos = criarProdutos(servico.executarGet())

  List<Produto> produtosValidos = new ArrayList<Produto>();
  for (Produto produto : produtos) {
    if (produto.temEstoque()) {
      produtosValidos.add(produto);
    }
  }
  return produtosValidos;
}

Note quantas linhas apenas a criação das configurações toma. Isso acaba desviando a atenção da responsabilidade principal do método, que é buscar os produtos preferenciais do usuário.

No exemplo anterior existem dois tipos de configurações, uma para o ambiente local e outra para os demais ambientes. A configuração dos ambientes remotos precisa lidar ainda com um feature toggle, para determinar qual versão da API deve ser utilizada.

A criação de configurações locais pode ser extraída como mostrado na Listagem 2. A identificação do recurso é comum a todos as configurações, portanto pode ser definida já no construtor da fábrica, assim como o id do usuário. As demais configurações são específicas ao ambiente local, ficando portanto dentro do método criarConfiguracaoLocal.

class FabricaConfiguracaoServicos {

  private ConfiguracoesServicos config;

  public FabricaConfiguracaoServicoProdutosPreferenciais(String idUsuario) {
    config = new ConfiguracoesServicoProdutosPreferenciais();
    config.setRecurso(“/produtos/preferenciais/”);
    config.setIdUsuario(idUsuario);
  }

  public ConfiguracoesServicos criarConfiguracaoLocal() {
    config.setEndpoint(“localhost:1234”);
    config.setXid(gerarUuid());
    config.setVersaoApi(“2.1”);
    return config;
  }
}

A lógica para criar as configurações de serviços para outros ambientes depende do feature toggle. Nesse caso vamos passar a informação se o toggle está ativo e o ambiente atual como parâmetros. A implementação pode ficar como na Listagem 3.

class FabricaConfiguracaoServicos {

  public ConfiguracoesServicos criarConfiguracaoRemota(boolean usarApiNova, Ambi\
ente ambiente) {
    if (usarApiNova) {
      config.setVersaoApi(“2.1”);
      config.setXid(gerarUuid());
    } else {
      config.setVersaoApi(“1.9”);
      config.setXid(“”);
    }
    config.setEndpoint(“https://intra.” + ambiente.toString() + “.megacorp.com”);
  }
}

Para utilizar o Simple Factory vamos simplesmente substituir o código dentro do if pela chamada ao método fábrica apropriado. Vamos também extrair essa parte em um novo método, para facilitar a leitura, como mostrado na Listagem 4:

public List<Produto> buscarProdutosPreferenciais(Usuario usuario) {
  ConfiguracoesServicos config = criarConfiguracaoDoAmbiente(usuario)

  ServicoRest servico = new ServicoRest(config);
  List<Produto> produtos = criarProdutos(servico.executarGet())

  List<Produto> produtosValidos = new ArrayList<Produto>();
  for (Produto produto : produtos) {
    if (produto.temEstoque()) {
      produtosValidos.add(produto);
    }
  }
  return produtosValidos;
}

public ConfiguracoesServicos criarConfiguracaoDoAmbiente(Usuario usuario) {
  FabricaConfiguracaoServicos fabrica = new FabricaConfiguracaoServicos(usuario.\
getId());

  if (getAmbienteAtual() == Ambiente.LOCAL) {
    return fabrica.criarConfiguracaoLocal();
  } else {
    return fabrica.criarConfiguracaoRemota(toggleApiNova.habilitado(), getAmbien\
teAtual());
  }
}

Apesar de simples, o ganho com a separação das responsabilidades é bem grande, principalmente na legibilidade do código.

Um Pouco de Teoria

Como mencionado antes, todos os padrões ajudam o seu código a seguir os princípios de design Orientado a Objetos. No caso do Simple Factory, o maior benefício é a divisão de responsabilidades seguindo o Princípio da Responsabilidade Única.

Quem lê o código do método buscarProdutosPreferenciais pode focar apenas em entender sua lógica, sem ter que se preocupar em como as configurações são criadas. Como passamos muito mais tempo lendo código do que escrevendo, essa é uma grande vantagem.

Outro benefício da separação dessas responsabilidades é que a maneira como as configurações do serviço são criadas vai mudar menos do que lógica definida em buscarProdutosPreferenciais. Ao lidar com dependências entre classes é sempre bom que uma classe dependa de outras menos prováveis de mudar.

Os testes do método buscarProdutosPreferencias também estão validando a criação das configurações de serviços, mesmo que indiretamente. Criando uma classe especializada para isso, podemos limitar o escopo dos testes e focar na lógica de negócio, testando a criação de configurações em outros testes isolados.

O Simple Factory é um bom ponto de início para separar a criação de objetos do seu uso. Como vimos no exemplo anterior, poucas classes são criadas e a estrutura do padrão é bem simples. Se o seu contexto permite isolar a maneira como objetos são criados e você tiver que lidar apenas com um tipo de objeto, o Simple Factory é uma excelente maneira de resolver o problema.

Anúncios

3 comentários sobre “O Padrão Simple Factory

  1. […] post anterior começamos a falar sobre o padrão Simple Factory, utilizando uma situação de exemplo para […]

  2. Francisco Chaves

    Então o Simple Factory seria basicamente dividir um grande método com uma sobrecarga de responsabilidade em vários métodos para facilitar a leitura e manutenção ?

    • Oi Francisco. Sim a ideia do Simple Factory é só extrair o método mesmo. Tanto que muitas pessoas nem consideram ele como um padrão de projeto, mas na real a complexidade da solução não é o que diz se é um padrão ou não.

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