Article image
Paulo Ferreira
Paulo Ferreira10/07/2025 19:42
Compartilhe

O padrão de projeto Abstract Factory e eficiência em Python e Java: qual a melhor linguagem?

  • #Java
  • #Python

Introdução

Você já ouviu falar na “Gang of Four”? A expressão é utilizada para se referir aos quatro autores do livro Design patterns – elements of reusable object-oriented software ou “Padrões de projeto - soluções reutilizáveis de software orientado a objetos”. Ele foi publicado em 1995 por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. 

Eles definem os padrões de projeto como “descrições de objetos e classes comunicantes que precisam ser personalizadas para resolver um problema geral de projeto num contexto particular”. Em outras palavras, são guias que oferecem alternativas de implementações focadas em dinamicidade, flexibilidade e eficiência. 

Imagine, por exemplo, um aplicativo de navegação GPS. Há incontáveis maneiras de chegar a um destino, mas uma rota pode fazer com que você chegue mais rápido, outra gastando menos combustível e outra evitando os semáforos. A ideia é a mesma, só que para engenharia de software.

Um programa pode utilizar dois, três ou até todos os padrões, a depender de sua complexidade e tamanho. Inicialmente concebidos para funcionar diretamente em relação ao paradigma orientado a objetos (como na linguagem Java), com o tempo a ferramenta também se mostrou poderosa para arquiteturas dinâmicas, como o Python.

Eles são divididos em três categorias: criacionais, comportamentais e estruturais. Aqui, vamos focar apenas em um padrão criacional. Mas espero que a leitura ajude você a querer aprender mais sobre essas soluções, que podem fazer toda a diferença no seu código.

Abstract Factory

Esse padrão tem o objetivo de criar famílias de objetos relacionados ou dependentes sem a necessidade de especificar suas classes concretas. Ela fornece uma interface para criar objetos que são da mesma família para que sejam compatíveis entre si.

Especialmente útil quando:

  • o sistema deve ser independente de como os produtos são criados ou representados;
  • o sistema deve ser montado com um produto de uma família de múltiplos produtos;
  • existe a necessidade de uma biblioteca de classes e produtos que revele somente as interfaces, não suas implementações.

Componentes:

  • AbstractFactory: interface para operações de criação dos produtos abstratos, declarando os métodos necessários;
  • ConcreteFactory: implementa a AbstractFactory, criando uma família específica de produtos;
  • AbstractProduct: interface para um tipo de objeto de produto;
  • ConcreteProduct: implementa a AbstractProduct definindo um objeto de produto a ser criado pela fábrica concreta correspondente.

Exemplo de uso em Python:

from abc import ABC, abstractmethod

# interface para os produtos:
class Camisa(ABC):
  @abstractmethod
  def vestir(self):
      pass

class Sapato(ABC):
  @abstractmethod
  def calcar(self):
      pass


# classe para os produtos concretos masculinos:
class CamisaMasculina(Camisa):
  def vestir(self):
      print("Vestindo camisa masculina.")

class SapatoMasculino(Sapato):
  def calcar(self):
      print("Calçando sapato masculino.")


# classe para os produtos concretos femininos:
class CamisaFeminina(Camisa):
  def vestir(self):
      print("Vestindo camisa feminina.")

class SapatoFeminino(Sapato):
  def calcar(self):
      print("Calçando sapato feminino.")


# interface para fabrica abstrata:
class FabricaModa(ABC):
  @abstractmethod
  def produzir_camisa(self) -> Camisa:
      pass

  @abstractmethod
  def produzir_sapato(self) -> Sapato:
      pass


# classe concreta para as fábricas:
class FabricaMasculina(FabricaModa):
  def produzir_camisa(self) -> Camisa:
      return CamisaMasculina()
  
  def produzir_sapato(self) -> Sapato:
      return SapatoMasculino()
  
class FabricaFeminina(FabricaModa):
  def produzir_camisa(self) -> Camisa:
      return CamisaFeminina()
  
  def produzir_sapato(self) -> Sapato:
      return SapatoFeminino()
  

# Cliente:
def vestir_e_calcar(fabrica: FabricaModa):
  camisa = fabrica.produzir_camisa()
  sapato = fabrica.produzir_sapato()
  
  camisa.vestir()
  sapato.calcar()


# Exemplo de uso:
if __name__ == "__main__":
  print("Moda Masculina:")
  fabrica_masculina = FabricaMasculina()
  vestir_e_calcar(fabrica_masculina)
  
  print("\nModa Feminina:")
  fabrica_feminina = FabricaFeminina()
  vestir_e_calcar(fabrica_feminina)

Implementação em Java:

Implementação das interfaces relativas aos produtos abstratos, no nosso caso, os tipos de roupas:

Camisa.java

package roupas;

public interface Camisa {
  void vestir();
}

Sapato.java

package roupas;

public interface Sapato {
  void calcar();
}

Agora as classes irão herdar das interfaces criadas anteriormente com os métodos definidos:

CamisaFeminina.java

package roupas.feminino;
import roupas.Camisa;

public class CamisaFeminina implements Camisa {
  @Override
  public void vestir() {
      System.out.println("Vestindo a camisa feminina.");
  }
}

SapatoFeminino.java

package roupas.feminino;
import roupas.Sapato;

public class SapatoFeminino implements Sapato {
  @Override
  public void calcar() {
      System.out.println("Calçando sapato feminino.");
  }
}

CamisaMasculina.java

package roupas.masculino;
import roupas.Camisa;

public class CamisaMasculina implements Camisa {
  @Override
  public void vestir() {
      System.out.println("Vestindo uma camisa masculina");
  }
}

SapatoMasculino.java

package roupas.masculino;
import roupas.Sapato;

public class SapatoMasculino implements Sapato {
  @Override
  public void calcar() {
      System.out.println("Calçando sapato masculino.");
  }
}

Agora precisamos da interface para criação de métodos relativos à família de roupas.

FabricaModa.java

package fabricas;
import roupas.Camisa;
import roupas.Sapato;

public interface FabricaModa {
  Camisa criarCamisa();
  Sapato criarSapato();
}

Finalmente, temos as fábricas concretas, que irão criar as roupas concretas:

FabricaFeminina.java

package fabricas;
import roupas.*;
import roupas.feminino.CamisaFeminina;
import roupas.feminino.SapatoFeminino;

public class FabricaFeminina implements FabricaModa {
  @Override
  public Camisa criarCamisa() {
      return new CamisaFeminina();
  }

  @Override
  public Sapato criarSapato() {
      return new SapatoFeminino();
  }
}

FabricaMasculina.java

package fabricas;
import roupas.*;
import roupas.masculino.CamisaMasculina;
import roupas.masculino.SapatoMasculino;

public class FabricaMasculina implements FabricaModa {
  @Override
  public Camisa criarCamisa() {
      return new CamisaMasculina();
  }

  @Override
  public Sapato criarSapato() {
      return new SapatoMasculino();
  }
}

Na classe Main, vamos ter um método para receber uma fábrica específica e criar as roupas de cada sexo e o método main, para criar de fato as roupas e calçados masculinos e femininos:

import roupas.*;
import fabricas.*;

public class Main {
  public static void novoLook(FabricaModa fabrica){
      Camisa camisa = fabrica.criarCamisa();
      Sapato sapato = fabrica.criarSapato();

      System.out.println("Novo look concluído.");
      camisa.vestir();
      sapato.calcar();
  }

  public static void main(String[] args) {
      System.out.println("Novo look masculino sendo montado agora...");
      novoLook(new FabricaMasculina());
      System.out.println("\n");

      System.out.println("Novo look feminino sendo montado agora...");
      novoLook(new FabricaFeminina());
  }
}

Tente replicar os códigos na sua IDE para ver os resultados!

Veredito

Com uma implementação segura, o Java oferece estabilidade e clareza, apesar da necessidade já conhecida de escrever mais linhas de código. Essa robustez pode ser essencial em projetos grandes, que envolvam a participação de equipes em diferentes níveis de complexidade. 

A linguagem pode ser mais verbosa, mas o contrato é claro. Ao utilizar as interfaces de fábrica, o cliente sabe exatamente qual tipo de roupa ou sapato o método deve retornar. Além disso, a utilização dos pacotes junto com o compilador ajuda o desenvolvedor a tratar erros de forma rápida.

Já o Python dispõe de velocidade e flexibilidade. A característica dinâmica intrínseca da linguagem permite fazer alterações de forma ágil. Isso significa que a prototipagem aqui também sai ganhando por conta da alta adaptabilidade. No entanto, a baixa segurança de tipos pode fazer com que um erro seja descoberto somente quando o código for executado. 

Aqui temos claramente um empate: se o seu trabalho envolver prioritariamente programas estáveis, detecção precoce de erros e previsibilidade, vá de Java. Se quer concisão, segurança em protótipos e flexibilidade, corra para o Python.

Compartilhe
Comentários (1)

AF

Antônio Félix - 10/07/2025 20:48

Se garantiu mano!

#uniforads