Padrão de fábrica abstrato - Abstract factory pattern
O padrão de fábrica abstrato fornece uma maneira de encapsular um grupo de fábricas individuais que têm um tema comum sem especificar suas classes concretas. Em uso normal, o software cliente cria uma implementação concreta da fábrica abstrata e, em seguida, usa a interface genérica da fábrica para criar os objetos concretos que fazem parte do tema. O cliente não sabe (ou se preocupa) com quais objetos concretos obtém de cada uma dessas fábricas internas, pois utiliza apenas as interfaces genéricas de seus produtos. Esse padrão separa os detalhes de implementação de um conjunto de objetos de seu uso geral e depende da composição do objeto, pois a criação do objeto é implementada em métodos expostos na interface de fábrica.
Um exemplo disso seria uma classe de fábrica abstrata DocumentCreator
que fornece interfaces para criar vários produtos (por exemplo, createLetter()
e createResume()
). O sistema teria qualquer número de versões concretas derivadas da DocumentCreator
classe como FancyDocumentCreator
ou ModernDocumentCreator
, cada uma com uma implementação diferente de createLetter()
e createResume()
que criaria um objeto correspondente como FancyLetter
ou ModernResume
. Cada um desses produtos é derivado de uma classe abstrata simples como Letter
ou Resume
da qual o cliente tem conhecimento. O código do cliente obteria uma instância apropriada de DocumentCreator
e chamaria seus métodos de fábrica . Cada um dos objetos resultantes seria criado a partir da mesma DocumentCreator
implementação e compartilharia um tema comum (todos seriam objetos sofisticados ou modernos). O cliente só precisaria saber como lidar com o resumo Letter
ou Resume
classe, não a versão específica que obteve da fábrica de concreto.
Uma fábrica é a localização de uma classe concreta no código em que os objetos são construídos . A intenção ao 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. Isso permite que novos tipos derivados sejam introduzidos sem nenhuma alteração no código que usa a classe base .
O uso desse padrão possibilita o intercâmbio de implementações concretas sem alterar o código que as usa, mesmo em tempo de execução . No entanto, o emprego desse padrão, como com padrões de design semelhantes , pode resultar em complexidade desnecessária e trabalho extra na escrita inicial do código. Além disso, níveis mais altos de separação e abstração podem resultar em sistemas mais difíceis de depurar e manter.
Visão geral
O padrão de projeto Abstract Factory é um dos vinte e três padrões de projeto GoF bem conhecidos que descrevem como resolver problemas de projeto recorrentes para projetar software orientado a objetos flexível e reutilizável, ou seja, objetos que são mais fáceis de implementar, alterar, testar, e reutilizar.
O padrão de projeto Abstract Factory resolve problemas como:
- Como um aplicativo pode ser independente de como seus objetos são criados?
- Como uma classe pode ser independente de como os objetos que ela requer são criados?
- Como podem ser criadas famílias de objetos relacionados ou dependentes?
Criar objetos diretamente dentro da classe que requer os objetos é inflexível porque compromete a classe para objetos específicos e torna impossível alterar a instanciação posteriormente, independentemente (sem ter que alterar) a classe. Isso impede que a classe seja reutilizável se outros objetos forem necessários e torna a classe difícil de testar porque objetos reais não podem ser substituídos por objetos fictícios.
O padrão de design Abstract Factory descreve como resolver esses problemas:
- Encapsule a criação do objeto em um objeto separado (fábrica). Ou seja, defina uma interface (AbstractFactory) para criar objetos e implemente a interface.
- Uma classe delega a criação do objeto a um objeto fábrica em vez de criar objetos diretamente.
Isso torna uma classe independente de como seus objetos são criados (quais classes concretas são instanciadas). Uma classe pode ser configurada com um objeto fábrica, que utiliza para criar objetos, e ainda mais, o objeto fábrica pode ser trocado em tempo de execução.
Definição
A essência do Abstract Factory Pattern é "Fornecer uma interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas."
Uso
A fábrica determina o tipo concreto real de objeto a ser criado, e é aqui que o objeto é realmente criado (em Java, por exemplo, pelo novo operador ). No entanto, a fábrica retorna apenas um ponteiro abstrato para o objeto concreto criado .
Isso isola o código do cliente da criação do objeto, fazendo com que os clientes peçam a um objeto fábrica para criar um objeto do tipo abstrato desejado e retornar um ponteiro abstrato para o objeto.
Como a fábrica retorna apenas um ponteiro abstrato, o código do cliente (que solicitou o objeto da fábrica) não sabe - e não está sobrecarregado - o tipo concreto real do objeto que acabou de ser criado. No entanto, o tipo de um objeto concreto (e, portanto, uma fábrica concreta) é conhecido pela fábrica abstrata; por exemplo, a fábrica pode lê-lo de um arquivo de configuração. O cliente não precisa especificar o tipo, pois ele já foi especificado no arquivo de configuração. Em particular, isso significa:
- O código do cliente não tem nenhum conhecimento do tipo concreto , não precisando incluir nenhum arquivo de cabeçalho ou declaração de classe relacionada a ele. O código do cliente lida apenas com o tipo abstrato. Objetos de um tipo concreto são de fato criados pela fábrica, mas o código do cliente acessa tais objetos apenas por meio de 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 normalmente é uma linha em um arquivo. A fábrica diferente então cria objetos de um tipo concreto diferente , mas ainda retorna um ponteiro do mesmo tipo abstrato de antes - isolando assim o código do cliente de mudanças. Isso é significativamente mais fácil do que modificar o código do cliente para instanciar um novo tipo, o que exigiria alterar cada local no código onde um novo objeto é criado (bem como certificar-se de que todos os locais de código também tenham conhecimento do novo tipo concreto, incluindo, por exemplo, um arquivo de cabeçalho de classe concreto). Se todos os objetos de fábrica forem armazenados globalmente em um objeto singleton e todo o código do cliente passar pelo singleton para acessar a fábrica adequada para a criação do objeto, alterar as fábricas é tão fácil quanto alterar o objeto singleton.
Estrutura
Diagrama UML
No diagrama de classes UML acima , a Client
classe que requer objetos ProductA
e ProductB
não instancia as classes ProductA1
e ProductB1
diretamente. Em vez disso, o Client
refere-se à AbstractFactory
interface para a criação de objetos, o que torna Client
independente de como os objetos são criados (quais classes concretas são instanciadas). A Factory1
classe implementa a AbstractFactory
interface instanciando as classes ProductA1
e ProductB1
.
O diagrama de sequência UML mostra as interações em tempo de execução: O Client
objeto chama createProductA()
o Factory1
objeto, que cria e retorna um ProductA1
objeto. Depois disso, as Client
chamadas createProductB()
em Factory1
, que cria e retorna um ProductB1
objeto.
Gráfico LePUS3
Exemplo Python
from abc import ABC, abstractmethod
from sys import platform
class Button(ABC):
@abstractmethod
def paint(self):
pass
class LinuxButton(Button):
def paint(self):
return "Render a button in a Linux style"
class WindowsButton(Button):
def paint(self):
return "Render a button in a Windows style"
class MacOSButton(Button):
def paint(self):
return "Render a button in a MacOS style"
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
class LinuxFactory(GUIFactory):
def create_button(self):
return LinuxButton()
class WindowsFactory(GUIFactory):
def create_button(self):
return WindowsButton()
class MacOSFactory(GUIFactory):
def create_button(self):
return MacOSButton()
if platform == "linux":
factory = LinuxFactory()
elif platform == "darwin":
factory = MacOSFactory()
elif platform == "win32":
factory = WindowsFactory()
else:
raise NotImplementedError(f"Not implemented for your platform: {platform}")
button = factory.create_button()
result = button.paint()
print(result)
Implementação alternativa usando as próprias classes como fábricas:
from abc import ABC, abstractmethod
from sys import platform
class Button(ABC):
@abstractmethod
def paint(self):
pass
class LinuxButton(Button):
def paint(self):
return "Render a button in a Linux style"
class WindowsButton(Button):
def paint(self):
return "Render a button in a Windows style"
class MacOSButton(Button):
def paint(self):
return "Render a button in a MacOS style"
if platform == "linux":
factory = LinuxButton
elif platform == "darwin":
factory = MacOSButton
elif platform == "win32":
factory = WindowsButton
else:
raise NotImplementedError(f"Not implemented for your platform: {platform}")
button = factory()
result = button.paint()
print(result)
Veja também
Referências
links externos
- Implementação de Abstract Factory em Java
- Mídia relacionada à fábrica de abstratos no Wikimedia Commons
- Diagrama UML Abstract Factory + especificação formal em LePUS3 e Class-Z (uma linguagem de descrição de projeto)
- Exemplo de implementação do Abstract Factory Abstract Factory