[go: up one dir, main page]

Saltar para o conteúdo

Abstract Factory

Origem: Wikipédia, a enciclopédia livre.

Abstract Factory é um padrão de projeto de software (também conhecido como design pattern em inglês). Este padrão permite a criação de famílias de objetos relacionados ou dependentes por meio de uma única interface e sem que a classe concreta seja especificada. Uma fábrica é a localização de uma classe concreta no código em que objetos são construídos . O objetivo em empregar o padrão é isolar a criação de objetos de seu uso e criar famílias de objetos relacionados sem ter que depender de suas classes concretas. Isto permite novos tipos derivados de ser introduzidas sem qualquer alteração ao código que usa a classe base . O uso deste padrão torna possível trocar implementações concretas sem alterar o código que estas usam, mesmo em tempo de execução. No entanto, o emprego deste padrão, como acontece com outros padrões semelhantes, pode resultar em uma complexidade desnecessária e trabalho extra no início do código. Além disso, os níveis mais elevados de abstração podem resultar em sistemas que são mais difíceis de manter. A essência do padrão Abstract Factory é fornecer uma interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas.

Diagrama UML do padrão Abstract Factory (Fábrica Abstrata)

Fábrica Abstrata

[editar | editar código-fonte]

Pode ser uma classe abstrata ou uma interface, mas a classe abstrata é utilizada com maior frequência. Seu objetivo é declarar métodos de criação de objetos do tipo ProdutoAbstrato, que são implementados por uma classe do tipo FabricaConcreta, que estende ou implementa a FabricaAbstrata.

Produto Abstrato

[editar | editar código-fonte]

Pode ser uma classe abstrata ou uma interface, mas a classe abstrata é utilizada com maior frequência. Produto abstrato declara os métodos que são implementados por classes do tipo ProdutoConcreto. FabricaConcreta cria internamente um objeto do tipo ProdutoConcreto, mas esse objeto é retornado como um ProdutoAbstrato. O Abstract Factory não sabe qual ProdutoConcreto está sendo criado, mas sabe quais métodos do produto ele pode utilizar.

Fábrica Concreta

[editar | editar código-fonte]

Estende ou implementa a FabricaAbstrata. O objetivo dessa classe é implementar os métodos declarados em FabricaAbstrata, criando um objeto do tipo ProdutoConcreto e retornando-o como um ProdutoAbstrato. Isso é polimorfismo. É comum existir mais de uma classe do tipo ProdutoConcreto assim como ocorre com FabricaConcreta. A quantidade de classes do tipo FabricaConcreta está diretamente ligada com a quantidade de classes do tipo ProdutoConcreto.

Produto Concreto(ProdutoA1, ProdutoA2, etc..)

[editar | editar código-fonte]

Estende ou implementa a classe ProdutoAbstrato. Nessa classe são implementados os métodos declarados em ProdutoAbstrato. Essa é a classe que faz uma instância concreta ser criada. Para cada FabricaConcreta, há pelo menos um ProdutoConcreto.

A fábrica determina o tipo concreto do objeto a ser criado, e é nela que o objeto é realmente criado. No entanto, a fábrica só retorna um ponteiro abstrato para o objeto concreto criado.

O código do cliente não tem conhecimento algum do tipo concreto. Objetos concretos são, de fato criados pela fábrica, mas o código do cliente acessa tais objetos só através da sua interface abstrata.

A adição de novos tipos concretos é feita modificando o código do cliente para usar uma fábrica diferente, uma modificação que é tipicamente uma linha em um arquivo. A nova fábrica, em seguida, cria objetos de um tipo de concreto diferente, mas ainda retorna um ponteiro do mesmo tipo abstrato como antes. Isto é significativamente mais fácil do que modificar o código de cliente para instanciar um novo tipo. Se todos os objetos de fábrica são armazenados globalmente em um objeto Singleton, e todo o código do cliente passa pelo Singleton para acessar a fábrica adequada para a criação do objeto, então alterar as fábricas se torna tão fácil como mudar o objeto Singleton.

Representação do diagrama em código Java

[editar | editar código-fonte]
public class FabricaAbstrataExemplo {

    public static void main(String[] args) {

        FabricaAbstrata fabrica1 = new FabricaConcreta1();
        Cliente cliente1 = new Cliente(fabrica1);
        cliente1.executar();

        FabricaAbstrata fabrica2 = new FabricaConcreta2();
        Cliente cliente2 = new Cliente(fabrica2);
        cliente2.executar();
    }
}

class Cliente {
    private ProdutoAbstratoA produtoA;
    private ProdutoAbstratoB produtoB;

    Cliente(FabricaAbstrata fabrica) {
        produtoA = fabrica.createProdutoA();
        produtoB = fabrica.createProdutoB();
    }

    void executar() {
        produtoB.interagir(produtoA);
    }
}

interface FabricaAbstrata {
    ProdutoAbstratoA createProdutoA();
    ProdutoAbstratoB createProdutoB();
}

interface ProdutoAbstratoA {

}

interface ProdutoAbstratoB {
    void interagir(ProdutoAbstratoA a);
}

class FabricaConcreta1 implements FabricaAbstrata {

    @Override
    public ProdutoAbstratoA createProdutoA() {
        return new ProdutoA1();
    }
    @Override
    public ProdutoAbstratoB createProdutoB() {
        return new ProdutoB1();
    }
}

class FabricaConcreta2 implements FabricaAbstrata {

    @Override
    public ProdutoAbstratoA createProdutoA() {
        return new ProdutoA2();
    }
    @Override
    public ProdutoAbstratoB createProdutoB() {
        return new ProdutoB2();
    }
}

class ProdutoA1 implements ProdutoAbstratoA {

}

class ProdutoB1 implements ProdutoAbstratoB {

    @Override
    public void interagir(ProdutoAbstratoA a) {
        System.out.println(this.getClass().getNome() + " interage com " + a.getClass().getNome());
    }

}

class ProdutoA2 implements ProdutoAbstratoA {

}

class ProdutoB2 implements ProdutoAbstratoB {

    @Override
    public void interagir(ProdutoAbstratoA a) {
        System.out.println(this.getClass().getNome() + " interage com " + a.getClass().getNome());
    }

}

Utilização (Interfaces Gráficas)

[editar | editar código-fonte]

O padrão Abstract Factory pode ser utilizado na implementação de um toolkit que disponibilize controles que funcionem em diferentes interfaces gráficas, tal como Motif, GTK+ (GNOME) ou Qt (KDE). Estas GUIs possuem diferentes padrões de controles visuais e, para facilitar a construção de aplicativos que interajam facilmente com diferentes interfaces gráficas, é interessante que se defina interfaces comuns para acesso aos controles, independentemente da GUI utilizada. Este problema pode ser resolvido por meio de uma classe abstrata que declara uma interface genérica para criação dos controles visuais e de uma classe abstrata para criação de cada tipo de controle. O comportamento específico, de cada um dos padrões tecnológicos contemplados, é implementado por meio de uma classe concreta. O aplicativo, ou "cliente", interage com o toolkit por meio das classes abstratas sem ter conhecimento da implementação das classes concretas.

Um exemplo bem simplista seria um projeto com interface para Mobile e para Desktop, uma boa opção para reaproveitar os mesmos controles de interface seria criar pacotes com classes abstratas e os pacotes com as classes concretas implementando apenas as diferenças. Esse padrão também se aplica na padronização de ambientes, por exemplo, tamanhos de botões, fontes, cores de fundo, largura de bordas. Com isso e havendo uma política que exija que os desenvolvedores usem essas classes em vez das nativas da linguagem, ajudará a padronizar a aparência e comportamento das aplicações.

Exemplo de Aplicação

[editar | editar código-fonte]

Neste exemplo, a classe abstrata WidgetFactory possui duas especializações: MotifWidgetFactory para widgets Motif e QtWidgetFactory para widgets Qt. Essas especializações são classes concretas capazes de "produzir" os elementos da interface gráfica. O cliente do toolkit obtém os elementos gráficos de que necessita por meio da classe (interface) WidgetFactory sem ter conhecimento das classes concretas. Da mesma maneira, o cliente somente interage com as interfaces que representam os elementos produzidos pela Abstract Factory (no exemplo, a classe Janela e a classe Botao).

Diagrama UML para retratar os exemplos de código abaixo.

Código em Java

[editar | editar código-fonte]

Este código, escrito na linguagem Java, mostra a implementação do diagrama mostrado acima. Por uma questão de simplicidade, o código relacionado às janelas é omitido.

 
 abstract class WidgetFactory
 {
     public static WidgetFactory obterFactory()
     {
         
         if( Configuracao.obterInterfaceGraficaAtual() == Configuracao.MotifWidget )
         {
             return new MotifWidgetFactory();
         }
         else
         {
             return new QtWidgetFactory();
         }
    }
 
    public abstract Botao criarBotao();
 }
 
 class MotifWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoMotif();
     }
 }
 
 class QtWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoQt();
     }
  }
 
 abstract class Botao
 {
     public abstract void desenhar();
 }
 
 class BotaoMotif extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Motif!");
     }
 }
 
 class BotaoQt extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Qt!");
     }
 }
 
 public class Cliente
 {
     public static void main(String[] args)
     {
         WidgetFactory factory = WidgetFactory.obterFactory();
 
         Botao botao = factory.criarBotao();
         botao.desenhar();
     }
 }

Este exemplo imprimiria na tela o texto "Eu sou um botao Motif!" ou "Eu sou um botao Qt!" dependendo do valor retornado pelo método Configuracao.obterInterfaceGraficaAtual(), que descobre a interface gráfica, Motif ou Qt, utilizada pelo sistema.

Código em VB.NET

[editar | editar código-fonte]
Public MustInherit Class WidgetFactory

    Shared Function obterFactory() As WidgetFactory
        If Configuracao.obterInterfaceGraficaAtual() Is Configuracao.MotifWidget Then
            Return New MotifWidgetFactory()
        Else
            Return New QtWidgetFactory()
        End If
    End Function

    Public MustOverride Function criarBotao() As Botao

End Class

Public Class MotifWidgetFactory
    Inherits WidgetFactory

    Public Overrides Function criarBotao() As Botao
        Return New BotaoMotif

    End Function
End Class

Public Class QtWidgetFactory
    Inherits WidgetFactory

    Public Overrides Function criarBotao() As Botao
        Return New BotaoQt
    End Function

End Class

Public MustInherit Class Botao

    Public MustOverride Sub desenhar()

End Class

Public Class BotaoMotif
    Inherits Botao

    Public Overrides Sub desenhar()
        Console.Out.WriteLine("Eu sou um botão Motif.")
    End Sub
End Class

Public Class BotaoQt
    Inherits Botao

    Public Overrides Sub desenhar()
        Console.Out.WriteLine("Eu sou um botão Qt.")
    End Sub
End Class

Public Class Client
    Public Shared Sub main()

        Dim factory As WidgetFactory = WidgetFactory.obterFactory

        Dim botao As Botao = factory.criarBotao

        botao.desenhar()

    End Sub
End Class

Padrões relacionados

[editar | editar código-fonte]