Desenvolvimento

10 dez, 2018

Arquitetura e desenvolvimento de software – Parte 03: Factory Method

Publicidade

Fala, pessoal! Tudo bem?

Continuando nossa viagem através dos 23 portais interdimensionais – ops, filme errado. Digo: através dos 23 Design Patterns definidos pelo GoF! Vamos abordar o próximo da lista, o Factory Method.

Factory Method

Também conhecido como construtor virtual, esse pattern permite às classes delegarem para subclasses decidirem, através da criação de objetos que chamam o método factory especificado numa interface e implementado por um classe filho ou implementado numa classe abstrata e opcionalmente sobrescrito por classes derivadas.

Calma, detalharemos isso para que possamos entender.

Criar um objeto geralmente exige processos complexos, não apropriados para incluir dentro da composição do objeto. A criação do objeto talvez necessite de uma duplicação de código significativa e de informações não acessíveis para a composição do objeto, ou então que não faça parte da composição do objeto.

O pattern Factory Method trata esses problemas definindo um método separado para criação dos objetos, no qual as subclasses possam sobrescrever para especificar o “tipo derivado” do produto que será criado. Daí o nome construtor virtual, pois este pode ser sobrescrito pelas classes de especialização.

Abaixo, podemos ver o exemplo em UML, citado no livro do GoF, onde temos quatro participantes:

  • Creator: declara o Factory Method que retorna o objeto da classe Product (abstrata)
  • ConcreteCreator: sobrescreve o Factory Method e retorna um objeto da classe ConcreteProduct
  • Product: define uma interface para os objetos criados pelo Factory Method
  • ConcreteProduct: uma implementação para a interface Product
Diagrama UML da estrutura de exemplo do pattern no livro do GoF

Esse padrão é muito utilizado em frameworks para definir e manter relacionamentos entre objetos. O framework Spring, dependendo da configuração, pode utilizar um Factory Method para criar seus Beans.

O objetivo do Factory Method está em diversas classes que implementam a mesma operação, retornam o mesmo tipo abstrato, mas internamente instanciam diferentes classes que o implementam. Com o Factory Method, o criador do objeto faz uma escolha de qual classe instanciar para o cliente.

Para ser um Factory Method, o método precisa retornar uma interface ou uma classe abstrata e, dependendo das necessidades do cliente, criar um objeto determinado como retorno. Um exemplo clássico do Factory Method são os iteradores, tanto em Java como em .NET.

Vamos a outro exemplo: uma aplicação, que é construída através de um framework baseado no padrão Factory Method, suporta a criação de documentos do tipo MeuDocumento. O framework é constituído pelas classes abstratas Aplicacao e Documento.

A aplicação disponibiliza as classes concretas MinhaAplicacao e MeuDocumento. A classe MinhaAplicacao é uma implementação da abstração definida pela classe Aplicacao.

Diagrama UML do exemplo Documento / Meu Documento

Ficha resumo

Vamos à “ficha” de definição deste pattern.

  • Nome: Factory Method;
  • Objetivo/intenção: criar um objeto geralmente requer processos complexos não apropriados para incluir dentro da composição do objeto; Também é conhecido como construtor virtual
  • Motivação: A criação do objeto talvez necessite de uma duplicação de código significativa, talvez necessite de informações não acessíveis para a composição do objeto, talvez não providencie um grau de abstração suficiente, ou então não faça parte da composição das preocupações do objeto. O Factory Method trata esses problemas definindo um método separado para criação dos objetos, no qual as subclasses possam sobrescrever para especificar o “tipo derivado” do produto que será criado
  • Aplicabilidade: esse padrão é muito utilizado em frameworks para definir e manter relacionamentos entre objetos. O framework Spring, dependendo da configuração, pode utilizar um Factory Method para criar os seus Beans. Normalmente, cenários em que precisamos de um construtor para um objeto padrão (abstrato), onde queremos implementar especializações mantendo uma base comum
  • Estrutura: abaixo, um exemplo de estrutura onde temos duas classes abstratas: o Creator e o Product, e suas especializações, ConcreteCreator e ConcreteProduct. O ConcreteCreator é capaz de criar um Product especializado. No caso, o ConcreteProduct, mantendo a base do Product (abstrato)
Estrutura UML para exemplificação do pattern
  • Consequências: ganhamos com o baixo acoplamento maior flexibilidade e eliminação da necessidade de acoplar classes específicas para aplicação em nível de código, mas temos alguns problemas que surgem, como o alto número de classes, que podem sobrecarregar o sistema, aumentando a quantidade de código a ser mantido
  • Implementações: conforme o exemplo no artigo, temos abaixo o código em C#, que demonstra a implementação do pattern para o exemplo da criação de objetos que venham da classe abstrata Documento, o MeuDocumento

Código C# da implementação do exemplo Documento/Meu Documento

public abstract class Aplicacao
{
    private Documento doc;

    Documento criaDocumento();

    void novoDocumento()
    {
        this.doc = this.criaDocumento();
    }
		

    void abrirDocumento()
    {
        this.doc.abrir();
    }

}

public abstract class Documento
{
    void abrir()
    {
        Console.WriteLine("Documento:Abrir documento!");
    }

    void fechar()
    {
        Console.WriteLine("Documento:Fechar documento!");
    }

    void gravar()
    {
        Console.WriteLine("Documento:Gravar documento!");
    }
}

public class MinhaAplicacao : Aplicacao
{
    public Documento criaDocumento()
    {
        return new MeuDocumento();
    }
}

public class MeuDocumento : Documento
{

}
  • Usos conhecidos: esse pattern é utilizado quando a classe não antecipa a classe do objeto que quer criar. Uma classe quer suas subclasses para especificar os objetos que cria, quando você não quer que o usuário tenha que saber de cada subclasse ou quando você quer encapsular a criação de objetos. O framework Spring usa amplamente esse pattern, normalmente associado com a injeção de dependência, onde temos métodos construtores de Beans, sendo que estes definem detalhes da instanciação destes Beans, retornando, normalmente, um tipo abstrato entre eles.
  • Padrões relacionados: Abstract Factory, Prototype e Template Method;

Conclusão

Esse foi o padrão Factory Method. O mesmo facilita bastante quando trabalhamos com aplicações que devem instanciar objetos, onde temos especializações de suas instanciações, mas trabalhamos com um retorno comum entre essas especializações.

Na próxima parte desta série vamos abordar o padrão Builder. Deixem dúvidas e feedbacks sobre a série nos comentários!

Até breve!